在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。

原文

相关文章
相关标签/搜索