在Android Studio中使用NDK/JNI - 实验版插件用户指南

介绍

新的实验版插件是基于Gradle的新组件模型机制的,它大大降低了配置时间。它还包含了NDK集成,以构建JNI应用程序。这份用户指南提供了关于如何使用它的详细信息,并特别指明了新的插件和原来的插件的不同。

警告:注意这个插件是插件的预览版,主要是为了获取关于性能和NDK的反馈。新组件模型的Gradle API还没有最终定稿,这意味每个插件都只能与一个特定版本的Gradle一起工作。

此外,DSL也可能改变。

最新版本

请检查bintray repository来获取最新版本。

要求(Requirements)

  • Gradle (请参考下面的部分来了解所需的版本) 
  • Android NDK r10e (如果你在使用NDK的话)
  • 具有最低为19.0.0Build ToolsSDK,我们的目标是使未来的迁移过程,能有最小化的改动数量。有些功能可能需要更近一些的版本。

Gradle要求

每个实验版插件需要一个特定版本的Gradle。这是所需的Gradle版本的列表。

 Plugin VersionGradle Version
0.1.02.5
 0.2.02.5
0.3.0-alpha32.6
0.4.02.8
0.6.0-alpha12.8
0.6.0-alpha52.10
0.7.0-alpha12.10

由传统的Android Gradle插件

一个典型的Android Studio工程可能具有下面这样的目录结构。需要修改的文件被特别标示了红色

新的插件和传统的插件之间在DSL上具有非常大的变化。

.
├── 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.properties

  • 每一个新插件的版本支持一个特定的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

 

./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。

原文

转载于:https://my.oschina.net/wolfcs/blog/550677

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值