在Android Studio中使用NDK/JNI - 實驗版插件用戶指南

介紹

新的實驗版插件是基於Gradle的新組件模型機制的,它大大下降了配置時間。它還包含了NDK集成,以構建JNI應用程序。這份用戶指南提供了關於如何使用它的詳細信息,並特別指明瞭新的插件和原來的插件的不一樣。java

警告:注意這個插件是插件的預覽版,主要是爲了獲取關於性能和NDK的反饋。新組件模型的Gradle API尚未最終定稿,這意味每一個插件都只能與一個特定版本的Gradle一塊兒工做。android

此外,DSL也可能改變。c++

最新版本

請檢查bintray repository來獲取最新版本。git

要求(Requirements)

  • Gradle (請參考下面的部分來了解所需的版本) 
  • Android NDK r10e (若是你在使用NDK的話)
  • 具備最低爲19.0.0Build ToolsSDK,咱們的目標是使將來的遷移過程,能有最小化的改動數量。有些功能可能須要更近一些的版本。

Gradle要求

每一個實驗版插件須要一個特定版本的Gradle。這是所需的Gradle版本的列表。github

 Plugin Version Gradle Version
0.1.0 2.5
 0.2.0 2.5
0.3.0-alpha3 2.6
0.4.0 2.8
0.6.0-alpha1 2.8
0.6.0-alpha5 2.10
0.7.0-alpha1 2.10

由傳統的Android Gradle插件

一個典型的Android Studio工程可能具備下面這樣的目錄結構。須要修改的文件被特別標示了紅色api

新的插件和傳統的插件之間在DSL上具備很是大的變化。app

.
├── app/
│   ├── app.iml
│   ├── build.gradle
│   └── src/
├── build.gradle
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew*
├── gradlew.bat
├── local.properties
├── MyApplication.iml
└── settings.gradle
性能

./gradle/wrapper/gradle-wrapper.propertiesgradle

  • 每個新插件的版本支持一個特定的Gradle版本。能夠參照上面的 Gradle要求 的部分來了解所要使用的Gradle版本。

#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
ui

 

./build.gradle

  • 插件的Classpath是com.android.tools.build:gradle-experimental,而不是com.android.tools.build:gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.

// 你能夠向頂層的構建文件中添加配置選項,這將用於全部的子項目/模塊。

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle-experimental:0.7.0-alpha4"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files

        // 注意:不要把你的應用程序的依賴放在這裏;它們屬於單獨的模塊的build.gradle文件
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

 

./app/build.gradle

插件的DSL有一些很是重要的變化。咱們理解這些變化中的許多使人感到費解,甚至彷佛是不須要的,而咱們的目標是移除當前這些變化中的一些以最小化在將來由傳統plugin到新版的遷移過程。

注意:相對於以前的版本,自版本0.6.0-alpha5起仍是有着很是重要的DSL提高。這裏的示例代碼將沒法在以前的版本上工做。若是你要使用一個更老版本的插件,請參考下面位置的用戶指南https://sites.google.com/a/android.com/tools/tech-docs/new-build-system/gradle-experimental/0-4-0

DSL的變化:

  • 插件的名字爲com.android.model.application,而不是com.android.application。若是你想要建立一個Android aar庫,則使用apply plugin: "com.android.model.library"
  • 配置由model { }塊包起來
  • 向一個Collection(集合)中添加元素應該使用add方法來完成。

當前DSL的以下一些限制有望消失:

  • List屬性只能用它們的直接類型來設置,而沒法接受其它的類型並適應他們。好比:你可使用一個String來設置一個類型爲File的屬性,但一個類型爲List<File>的屬性只接受File對象。
  • 建立一個buildType或productFlavor須要調用create方法。修改已有的,好比release和debug buildType能夠只使用名字來完成。
  • 如今修改variants及它們的tasks的DSL很是很是受限的。

apply plugin: "com.android.model.application"

model {
    android {
        compileSdkVersion = 23
        buildToolsVersion = "23.0.2"

        defaultConfig {
            applicationId "com.example.user.myapplication"
            minSdkVersion.apiLevel 15
            targetSdkVersion.apiLevel 22
            versionCode 1
            versionName "1.0"

            buildConfigFields {
                create() {
                    type "int"
                    name "VALUE"
                    value "1"
                }
            }
        }

        buildTypes {
            release {
                minifyEnabled false
                proguardFiles.add(file("proguard-rules.pro"))
            }
        }
        productFlavors {
            create("flavor1") {
                applicationId "com.app"
            }
        }
    
        // Configures source set directory.
        sources {
            main {
                java {
                    source {
                        srcDir "src"
                    }
                }
            }
        }

    }

}

dependencies {
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile "com.android.support:appcompat-v7:22.2.0"
}

簽名配置

你可使用$()語法引用另外一個模型元素。要使用這一語法"-Dorg.gradle.model.dsl=true"應該做爲參數被添加到版本在2.10如下的Gradle的命令行上。這對於指定簽名配置頗有用。注意:當前android.signingConfigs必需放在android {} 塊的外面。

apply plugin: "com.android.model.application"

model {
    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.2"
        buildTypes {
            release {
                signingConfig = $("android.signingConfigs.myConfig")
            }
        }
    }
    android.signingConfigs {
        create("myConfig") {
            storeFile "/path/to/debug.keystore"
            storePassword "android"
            keyAlias "androiddebugkey"
            keyPassword "android"
            storeType "jks"
        }
    }
}

 

Ndk集成

實驗性插件包含了NDK集成,以建立native應用。要使用NDK集成,則:

  • 使用Studio內的SDK Manager下載NDK。
  • 在local.properties中設置ndk.dir或設置ANDROID_NDK_HOME環境變量指向包含NDK的目錄。
  • 在build.gradle中向model添加一個android.ndk塊。

一個簡單的NDK應用的build.gradle看起來多是這樣的:

apply plugin: 'com.android.model.application'

model {
    android {
        compileSdkVersion = 23
        buildToolsVersion = "23.0.2"

        ndk {
            moduleName = "native"
        }
    }
}

*注意moduleName是必須的。它決定了最終native library的名字。

源碼集合

默認狀況下,它會到src/main/jni下去找C/C++文件。但能夠配置android.sources來改變源碼目錄。

model {
    android {
        compileSdkVersion = 23
        buildToolsVersion = "23.0.2"

        ndk {
            moduleName = "native"
        }

        sources {
            main {
                jni {
                    source {
                        srcDir "src"
                    }
                }
            }
        }
    }
}

JNI源碼集可能同時包含了C和C++文件。子目錄中的全部文件都會被包含。文件擴展名是'.c'的會被看成是C文件,而C++文件則可能具備下面這些擴展名中的一種:'.C','.CPP','c++','.cc','.cp','.cpp','.cxx'。文件能夠經過exclude方法來剔除,從而在include時被忽略:

model {    
    android.sources {
        main {
            jni {
                source {
                    include "someFile.txt"  // This is ignored.
                    exclude "**/excludeThisFile.c"
                }
            }
        }
    }
}

這種指定源碼的根目錄,而後用exclude方法剔除不須要編譯的文件的方法,與Eclipse環境下在Android.mk文件中經過LOCAL_SRC_FILES變量指定要編譯的文件的方式很是不同。

其它的構建選項

能夠在android.ndk { }塊內設置不一樣的構建選項。好比,

model {
    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.2"
        ndk {
            // All configurations that can be changed in android.ndk.
            moduleName "native"
            toolchain "clang"
            toolchainVersion "3.5"
            // Note that CFlags has a capital C, which is inconsistent with
            // the naming convention of other properties.  This is a
            // technical limitation that will be resolved
            CFlags.add("-DCUSTOM_DEFINE")
            cppFlags.add("-DCUSTOM_DEFINE")
            ldFlags.add("-L/custom/lib/path")
            ldLibs.add("log")
            stl "stlport_static"
        }
        buildTypes {
            release {
                ndk {
                    debuggable true
                }
            }
        }
        productFlavors {
            create("arm") {
                ndk {
                    // You can customize the NDK configurations for each
                    // productFlavors and buildTypes.
                    abiFilters.add("armeabi-v7a")
                }
            }
            create("fat") {
                // If ndk.abiFilters is not configured, the application
                // compile and package all suppported ABI.
            }
        }
    }

    // You can modify the NDK configuration for each variant.
    components.android {
        binaries.afterEach { binary ->
            binary.mergedNdkConfig.cppFlags.add(
                    "-DVARIANT=\"" + binary.name + "\"")
        }
    }
}

咱們日常見到的更多的場景是,將用到了NDK的Eclipse工程遷移到Android Studio,須要用build.gradle的配置來替換原來Android.mk和Application.mk這兩個文件中的設置的情形。對於咱們的這種需求,上面的這個例子能夠爲咱們提供許多的幫助。

CFlags和cppFlags能夠用來替換原來Android.mk中的LOCAL_CFLAGS和LOCAL_C_INCLUDES,以及Application.mk中的APP_CPPFLAGS。不過這裏必定要注意,CFlags和cppFlags分別應用於C程序文件和C++程序文件的編譯,好比項目中同時包含C程序文件和C++程序文件,對於某一個頭文件,在C程序文件和C++程序文件中都有用到,則須要將該頭文件的路徑同時添加進CFlags和cppFlags。

ldLibs能夠用來替換原來Android.mk中的LOCAL_LDLIBS。

stl能夠替換原來Application.mk中的APP_STL。

abiFilters能夠替換原來Application.mk中的APP_ABI。

已知的限制

  • 不支持使用一個NDK modules,好比cpu_features
  • 不支持集成外部的構建系統

示例

能夠在https://github.com/googlesamples/android-ndk找到其它的一些示例。

多NDK工程

Plugin 0.4.0爲NDK依賴添加了初步的支持及建立一個native library的能力。但請注意這只是咱們前進的方向上的一個預覽版本,實現尚未完成。注意,儘管編譯Gradle的native工程是可能的,但在Android Studio中的編輯和調試支持尚未實現。

獨立的NDK插件

在gradle-experimental:0.4.0中,建立了一個新的插件以容許在不建立一個Android應用程序和庫的狀況下只建立native庫。DSL與application/library插件相似。下面的例子build.gradle能夠由"src/main/jni"下的代碼建立一個libhello.so。

apply plugin: "com.android.model.native"

model {
    android {
        compileSdkVersion 23
        ndk {
            moduleName "hello"
        }
    }
}

已知的問題

  • Android Studio中獨立插件的編輯支持尚未實現。
  • 修改庫中的一個源文件不會致使應用程序在構建時自動地從新連接新的庫。

NDK依賴

指定依賴的語法符合Gradle將來的依賴系統的風格。你能夠設置對於一個Android工程或一個特定的文件的依賴。

好比,你在"lib"下有一個子工程使用了獨立NDK插件:

lib/build.gradle:

apply plugin: "com.android.model.native"

model {
    android {
        compileSdkVersion 23
     ndk {
         moduleName "hello"
     }
     sources {
         main {
             jni {
                 exportedHeaders {
                     srcDir "src/main/headers"
                 }
             }
         }
     }
    }
}

任何有一個JNI依賴的工程將包含exportedHeaders中指定的目錄。你能夠在你的應用中爲你的JNI代碼添加對於lib工程的依賴:

app/build.gradle:
apply plugin: "com.android.model.application"


model {
    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.2"
        sources {
            main {
                jni {
                    dependencies {
                        project ":lib1"
                     }
                }
            }
        }
    }
}

你能夠指定你的目標工程的build type和/或product flavor。不然插件將試着尋找與你的應用程序相同的build types和product flavor。若是你想要native庫被靜態地連接的話,你也能夠指定linkage類型。好比:

model {
    android.sources {
        main {
            jni {
                dependencies {
                    project ":lib1" buildType "debug" productFlavor "flavor1" linkage "static"
                }
            }
        }
    }
}

要聲明對一個文件的依賴,建立一個預編譯庫,並添加對於該庫的依賴。好比,

model {
    repositories {
        libs(PrebuiltLibraries) {
            prebuilt {
                headers.srcDir "path/to/headers"
                binaries.withType(SharedLibraryBinary) {
                    sharedLibraryFile = file("lib/${targetPlatform.getName()}/prebuilt.so")
                }
            }
        }
    }
    android.sources {
        main {
            jniLibs {
                dependencies {
                    library "prebuilt"
                }
            }
        }
    }
}

警告:下個版本將有一個DSL的改動,使得Gradle能夠內建支持預編譯的庫,相似於https://github.com/gradle/gradle/blob/master/subprojects/docs/src/samples/native-binaries/prebuilt/build.gradle。

你能夠爲'jniLibs'或'jni' source set添加native依賴。當給'jniLibs'添加了依賴時,native library將會被打包進application/library,但它不會被用於編譯JNI代碼。好比:

model {
    android.sources {
        main {
            jniLibs {
                dependencies {
                    library "prebuilt"
                }
            }
        }
    }
}

更多的應用場景應該是,咱們本身的JNI代碼依賴於另外的一個so,不只僅須要在編譯時讓構建系統清楚這種依賴,還須要將這個so打包進application/library。對於這種狀況,在'jni' source set添加native依賴是必不可少的,但也不能省略了'jniLibs'中對於該so的依賴的設置。不然就是,代碼可以編譯經過,但app在運行期間,load library的時候會crash,報出找不到某個so文件這樣的錯誤。

能夠看到'jni' source set添加native依賴能夠替換Eclipse工程裏Android.mk中的LOCAL_SHARED_LIBRARIES和LOCAL_STATIC_LIBRARIES。

DSL的改變

這個插件仍然處於實驗階段。DSL將在插件的開發過程當中改變。這個部分描述了發生在不一樣版本之間的改動,以幫助遷移。

0.6.0-alpha1 -> 0.6.0-alpha5

  • 插件如今須要有Gradle 2.10,這一版Gradle給DSL帶來了很是大的提高
  • 如今配置可嵌套了。好比,你能夠這樣寫

android {
    buildTypes {
        ...
    }
}

而不是

android.buildTypes {
    ...
}

  • File類型如今能夠接受一個string,但如今還不能把String添加進List<File>中。
  • 如今-Dorg.gradle.model=true是默認的了。這容許引用其餘模型,但被引用的模型必須在另外的塊中。
  • 等號'='對於大多數屬性都再也不須要了。

0.4.x -> 0.6.0-alpha1

  • 描述對於一個特定的庫文件的依賴的DSL已經改成了下面的Gradle的native依賴DSL了。(參考 https://github.com/gradle/gradle/blob/master/subprojects/docs/src/samples/native-binaries/prebuilt/build.gradle)

model {
    android.sources {
        main {
            jniLibs {
                dependencies {
                    library file("lib/x86/prebuilt.so") abi "x86"
                    library file("lib/armeabi-v7a/prebuilt.so") abi "armeabi-v7a"
                    library file("lib/mips/prebuilt.so") abi "mips"
                }
            }
        }
    }
}


被替換爲了:


model {
    repositories {
        prebuilt(PrebuiltLibraries) {
            binaries.withType(SharedLibraryBinary) {
                sharedLibraryFile = file("lib/${targetPlatform.getName()}/prebuilt.so")
            }
        }
    }
    android.sources {
        main {
            jniLibs {
                dependencies {
                    library "prebuilt"
                }
            }
        }
    }
}

0.2.x -> 0.4.0

  • += no longer works for collections.  +=對於集合再也不起做用了。添加items到列表能夠經過'add'或'addAll'方法來完成。好比CFlags += "-DCUSTOM_DEFINE"能夠用CFlags.add("-DCUSTOM_DEFINE")替換爲。

0.1.x -> 0.2.x

  • jniDebuggable被從build type配置中移除了,被移到了ndk塊。好比:


release {
    jniDebuggable = true
}
becomes
release {
    ndk.with {
        debuggable = true
    }
}

Change Log

0.6.0-alpha3

  • 指定對於預編譯庫的依賴的DSL已經改變了。
  • 更新到了Gradle 2.8。
  • 解決了多個native庫依賴解析的問題。

0.4.0

  • 解決了在實驗版庫插件中使用jni代碼的問題。
  • 容許platform版本被設置爲與compileSdkVersion不一樣的值。
  • Allow ABI specific configurations in a variant that contains multiple ABI.
  • 添加了支持,對於NDK插件和shared object/static library文件的依賴。
  • A preview version of an standalone plugin for compiling just native code is now available.  It can be use to build application with Gradle, but support in Android Studio is not yet implemented.

Done。

原文

相關文章
相關標籤/搜索