如何用Android Gradle插件0.7configurationNDK

新的Android gradle插件(0.7)似乎包含了对NDK的新支持,但在文档中几乎没有提及它(我发现的唯一参考是一个名为ndkSanAngeles的testing)。

它看起来像gradle正在寻找NDK,我已经包括在我的path。 但是,build设项目失败了

  • 出了什么问题:执行失败的任务':OGLTests:compileDefaultFlavorDebugNdk'。 NDK未configuration

如何在gradle中configurationNDK?

我目前的build.gradle看起来像这样:

 task nativeLibsToJar(type: Zip, description: 'create a jar with native libs') { destinationDir file("$buildDir/native-libs") baseName 'native-libs' extension 'jar' from fileTree(dir: 'src/main/libs', include: '**/*.so') from fileTree(dir: 'src/main/libs', include: '**/gdb*') into 'lib/' } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn nativeLibsToJar } dependencies { compile fileTree(dir: "$buildDir/native-libs", include: '*.jar') } android { compileSdkVersion 19 buildToolsVersion '19.0.0' defaultConfig { minSdkVersion 14 targetSdkVersion 19 versionCode 1 versionName "0.1" } buildTypes { release { runProguard false } debug { // jniDebugBuild true runProguard false debuggable true } } productFlavors { defaultFlavor { proguardFile 'proguard-rules.txt' } } } 

谢谢。

通过gradle插件代码,我发现以下内容帮助我使用NDK和预build的本机库:

要简单链接到预build的本机库 ,只需在你的任务中添加一个ndk部分即可。 例如,我在productFlavors中添加了它。 abiFilter意味着库文件夹的名称.abiFilters意味着从逗号分隔的列表中的两个库将被添加到你的最终APK(所以你理论上可以有“armeabi”,“armeabi-v7a”,“x86”和“ mips“全部在一个APK中,并且O / S将在安装时select支持的架构库):

 productFlavors { arm { ndk { abiFilters "armeabi", "armeabi-v7a" } } x86 { ndk { abiFilter "x86" } } } 

在这个例子中,arm版本将创build一个V5和V7A arm库的APK,x86版本将创build一个只有x86库的APK。 这将search项目jniLibs目录中的本地库。 jniLibs目录应该是旧的jni目录结构,即:

 [project]/[app]/src/main/jniLibs/armeabi/libmyNative.so [project]/[app]/src/main/jniLibs/armeabi-v7a/libmyNative.so [project]/[app]/src/main/jniLibs/x86/libmyNative.so 

然后你可以用Java加载它,如下所示:

 static { loadLibrary("myNative"); } 

现在,让我们说一个本地库依赖于另一个。 您必须(如果将您的最小API设置为API 17或更低)首先加载相关的库:

 static { loadLibrary("myDependency"); loadLibrary("myNative"); } 

您也可以将ndk {}部分放置在您的defaultConfig或buildType中(例如debugging或发行版或其他您可能使用的任何其他部分)。 例如:

 buildTypes { debug { ndk { abiFilters "armeabi", "armeabi-v7a" } } } 

通过预编译,我指的是下载的第三方库或使用NDK工具链或您自己的ARM工具链(而不是ndk-build脚本本身)构build的库。

在API 18中,他们修复了一个长期存在的架构问题,阻止了本地lib加载器“自动”加载依赖关系,因为它不知道应用程序的lib目录(安全原因等)。 在API 18及以上版本中,如果myNative依赖于上面的myDependency,则可以调用loadLibrary(“myNative”),OS将处理加载myDependency。 尽pipe如此,直到运行API 17及以下版本的设备的市场渗透率达到您所能接受的最低水平为止。

要在Android Studio的当前版本中显式构build来自Source的NDK库 ,您可以执行以下操作:

如前所述,将local.properties中的ndk.dir值设置为指向NDK主目录。 有谁知道你是否可以直接在local.properties中使用envvariables? 🙂

在你的build.gradle文件中,把这样的东西添加到你的任务(可以是defaultConfig,debug,release,productFlavor等等):

 ndk { moduleName "myNDKModule" stl "stlport_shared" ldLibs "log", "z", "m" cFlags "-I/some/include/path" } 

这是目前支持的types(moduleName,stl,ldLibs和cFlags)的基本结构。 我看了,没有发现更多。 我相信ldLibs存在一个问题,因为它会自动将“-l”添加到上面每个字段的前面。 你可以欺骗它(我不得不说):ldLibs“log -lz -lm -Wl,-whole-archive -l / path / to / someOtherLib -Wl,-no-whole-archive”

在这一行中,您只需标记第一个参数的末尾即可添加不以-l开头的参数,以便您现在就可以开始使用。 在上面的例子中,我将整个静态库链接到我的NDK模块中,以便在Java中使用。 我已经要求谷歌开发人员添加额外的function,以允许这个甚至能够将自己的Android.mk文件合并到NDK构build过程,但是这是全新的,可能是一段时间。

目前,无论你在build.gradle中删除temp build目录并且每次都重新创build它,所以除非你想下载和修改gradle android插件源代码(这很有趣),还有一些“make due”像这需要把你的东西复制到构build。 本质上提供这个ndk支持的android gradle脚本会生成一个Android.mk文件,并在临时目录中使用NDK系统进行构build。

侧面一秒。 moduleName应该在jni目录下的项目中匹配ac或cpp文件,例如:

 [project]/[app]/src/main/jni/myNDKModule.cpp 

如果要为C ++使用stlport库,则应将stl值设置为“stlport_shared”或“stlport_static”的值。 如果你不需要扩展的C ++支持,你可以离开stl。 记住Android默认提供非常基本的C ++支持。 对于其他受支持的C ++库,查看您下载的NDK中的NDK文档指南。 请注意,通过将其设置为stlport_shared,gradle会将libstlport_shared.so lib从您的NDK的sources / cxx-stl / stlport / libs目录复制到APK的lib目录中。 它还处理编译器中的包含path(从技术上来说,gradle并不是所有这些,而是​​Android NDK构build系统)。 所以不要把你自己的stlport拷贝到你的jniLibs目录中。

最后,我认为cFlags非常明显。

你不能在Mac OSX上设置ANDROID_NDK_HOME(见下文),但是从我做的一些研究看来,也许这在其他操作系统上仍然有效。 它将被删除。

我想发表评论,但还没有名声。 丹尼斯,环境variables被完全忽略,不仅被覆盖。 事实上,你没有得到任何你的环境variables。 从我所知道的情况来看,Android Studio IDE使用一些特定的环境variables创build自己的环境(检查System.getenv()并从Gradle脚本中打印出来)。

我在这里写了一个错误,因为使用env vars可以从cmd行生成:
https://code.google.com/p/android/issues/detail?id=65213

但正如你所看到的,Google决定他们不希望IDE使用环境variables; 我仍然在这个决定上。 这使得我的生活很痛苦,必须更新local.properties指向可以在我的gradle脚本中加载的绝对path,我还没有想出如何做(但没有看起来很难)。 这意味着我要么迫使我的团队成员使用与我一样的path,使用链接,使他们每次拉回购时都键入它们,或者添加一个自动化脚本。 我相信这是一个糟糕的决定,任何依赖envvariables的开发人员都可能花费时间,这些variables在微观层面上可能很小,但在macros观层面却很大。

groundloop,我相信IDE将很快更新,能够添加NDK文件夹path到你的项目,它会自动生成local.properties文件(至less如果他们没有想到是没有意义的这个)。

有关Google的更多详细示例,请参阅以下示例(searchjni或ndk): https ://docs.google.com/viewer ?a = v& pid = sites & srcid = YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA

使用NDK的跨平台胖APK:

最后,使用gradle有一个缺点,不能提供你自己的Android.mk文件,这样你就只能将第三方本地库从单一的体系结构链接到你的NDK。 注意我说“链接在”。 您可以使用“abiFilters”命令在多个体系结构中构buildNDK模块(上面的moduleName),并将它们放置在您的应用中,以便可以在多个体系结构上使用相同的APK。 如果你需要连接你自己的第三方库,甚至根据你的体系结构有不同的cFlags值,那就不是一个简单的方法。

我尝试了下面这个,看起来起初工作,但后来我发现它只是通过从两个NDK部分一起附加所有东西来构buildNDK(或类似的东西,但它以某种方式构build了多个架构库):

 android { compileSdkVersion 23 buildToolsVersion '23.0.1' defaultConfig { minSdkVersion 14 targetSdkVersion 23 versionCode 28 versionName "3.0" } buildTypes { def commonLibs = " -lfoo -lbar -lwhatever" def armV7LibsDir = "/whatever/armv7a/libs" def armX86LibsDir = "/whatever/x86/libs" def armV7IncDir = "/whatever/armv7a/include" def x86IncDir = "/whatever/x86/include" debug { ndk { cFlags = "-I" + armV7IncDir moduleName "myNativeCPPModule" stl "stlport_shared" abiFilter "armeabi-v7a" ldLibs "log -L" + armV7LibsDir + commonLibs } ndk { cFlags = "-I" + armX86IncDir moduleName "myNativeCPPModule" stl "stlport_shared" abiFilter "x86" ldLibs "log -L" + armX86LibsDir + commonLibs } } } } 

在尝试在一个拥有Gradle和原生第三方库的干净庄园中创build一个胖二进制文件之后,我终于得出结论,Google Play内置的多架构APK对于反正是最好的路线,所以创build每个架构的个人APK。

所以我创build了多个buildType,没有产品风格,并添加了下面的代码来为每种types生成版本代码。

 // This is somewhat nasty, but we need to put a "2" in front of all ARMEABI-V7A builds, a "3" in front of 64-bit ARM, etc. // Google Play chooses the best APK based on version code, so if a device supports both X86 and // ARM, it will choose the X86 APK (preferred because Inky ARM running on an X86 with Houdini ARM Emulator crashes in our case) android.applicationVariants.all { variant -> if (variant.buildType.name.equals('release')) { variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('debug')) { variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('debugArmV8a')) { variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('releaseArmV8a')) { variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('debugMips')) { variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('releaseMips')) { variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('debugMips64')) { variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('releaseMips64')) { variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('debugX86')) { variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('releaseX86')) { variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('debugX86_64')) { variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode } else if (variant.buildType.name.equals('releaseX86_64')) { variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode } } 

现在,您只需要在defaultConfig对象中设置versionCode的值,就像您通常所做的一样,然后将其附加到基于构buildtypes的特定于体系结构的版本string的末尾。 所有版本的版本string保持不变,但改变代码以提供从ARM到X86_64的优先顺序。 这有点冒险或硬编码,但它完成了工作。 请注意,这提供了多达999个版本,所以如果您需要更多,请将上面的数字乘以10,不知道您可以放入版本代码的最大值是多less。

就我而言,我们有一个相当复杂的构build系统。 我们为9个体系结构构build了CPython,其中3个是Android,然后构build了一大堆我们自己的库,并将它们连接在一起,形成每个体系结构的单个库。 我们使用ndk命令行构build工具,automake和python来构build一切,而不是Android.mk文件。 然后将最终的库链接到单个JNI接口cpp文件(以上称为myNativeCPPModule)。 只需点击一下button,一切都一次build成,非常漂亮的Android Studio。

find答案。 在local.properties文件中包含ndk.dir=path/to/ndk就行了。

更新:在最新版本的Android Studio上,您可以直接在“项目结构”>“SDK位置”中设置值。

你也可以设置ANDROID_NDK_HOME环境variables

我花了很多时间在build.gradle中configurationndk。 我有一个很好的博客解决我的问题。

如前所述,在local.properties中添加ndk.dir =有帮助。 有趣的是,我发现local.properties覆盖了为环境variablesANDROID_NDK_HOME设置的任何值, 即使你没有在local.properties中configurationndk.dir也是如此 。 (至less用gradle android插件v 0.7.3)。

这是混乱的Android Studio可以覆盖local.properties,似乎并没有提供一种方式来configurationndk.dir 🙁

Android工作室build议在local.properties中包含ndk的path