新的Android gradle插件(0.7)似乎包含对NDK的新支持,但在文档中几乎没有提及它(我发现的唯一参考是名为ndkSanAngeles
的测试。)< / p>
看起来gradle正在寻找我已经包含在我的PATH中的NDK。但是,使用
构建项目失败
- 出了什么问题: 任务执行失败':OGLTests:compileDefaultFlavorDebugNdk'。 NDK未配置
如何在gradle中配置NDK?
我当前的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'
}
}
}
感谢。
答案 0 :(得分:76)
通过gradle插件代码,我发现以下内容帮助我使用了NDK和预构建的本机库:
要简单地在预建本地库中链接,只需在任务中添加一个ndk部分即可。例如,我在productFlavors中添加了它。 abiFilter是libs存储的文件夹名称.abiFilters意味着来自逗号分隔列表的libs将被添加到你的最终APK中(理论上你可以有#34; armeabi&#34;,&#34; armeabi-v7a& #34;,&#34; x86&#34;和&#34; mips&#34;所有在一个APK中,O / S将在安装时选择支持的体系结构lib:
productFlavors {
arm {
ndk {
abiFilters "armeabi", "armeabi-v7a"
}
}
x86 {
ndk {
abiFilter "x86"
}
}
}
在这个例子中,arm构建将创建一个带有V5和V7A arm库的APK,而x86构建将创建一个仅包含x86库的APK。这将搜索项目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中(例如调试或发布或您可能使用的任何其他内容)。例如:
buildTypes {
debug {
ndk {
abiFilters "armeabi", "armeabi-v7a"
}
}
}
通过预先构建,我指的是您下载的第三方库或使用NDK工具链或您自己的ARM工具链(而不是ndk-build脚本本身)构建的库。
在API 18中,他们修复了一个长期存在的架构问题,该问题阻止了本机lib加载程序自动生成#34;加载依赖项,因为它不知道应用程序的lib目录(安全性原因等)。在API 18及更高版本中,如果myNative依赖于上面的myDependency,您只需调用loadLibrary(&#34; myNative&#34;),操作系统将处理加载myDependency。不过,请不要这样做,直到运行API 17及更低版本的设备的市场渗透率达到您可接受的低数量。
要在当前版本的Android Studio中明确从源构建NDK库,您可以执行以下操作:
如前所述,将local.properties中的ndk.dir值设置为指向NDK home。有谁知道你是否可以直接在local.properties中使用env vars? :)
在你的build.gradle文件中,为你的任务添加这样的东西(同样,可以是defaultConfig,debug,release,productFlavor等):
ndk {
moduleName "myNDKModule"
stl "stlport_shared"
ldLibs "log", "z", "m"
cFlags "-I/some/include/path"
}
这是当前支持的类型(moduleName,stl,ldLibs和cFlags)的基本结构。我看起来并没有找到更多。我相信ldLibs存在一个问题,因为它会自动添加&#34; -l&#34;到了上面每个场地的前面。你可以欺骗它(我不得不)说: ldLibs&#34; log -lz -lm -Wl,-whole-archive -l / path / to / someOtherLib -Wl,-no-whole-archive&#34;
在这一行中,您只是标记到第一个参数的末尾,以添加不以-l开头的参数,这样您就可以暂时使用了。在上面的例子中,我将整个静态库链接到我的NDK模块,以便在Java中使用。我已经要求谷歌开发人员添加其他功能,以允许甚至将您自己的Android.mk文件合并到NDK构建过程中,但由于这是全新的,可能需要一段时间。
目前,无论你在build.gradle中放置什么都删除了临时构建目录并且每次都重新创建它,所以除非你想下载和修改gradle android插件源代码(这会很有趣),还有一些&#34因为这需要将你的东西复制到构建中。提供此ndk支持的android gradle脚本实质上会生成一个Android.mk文件,并使用NDK系统在临时目录中构建。
Sidetracked一秒钟。 moduleName应该与jni目录下的项目中的c或cpp文件匹配,如:
[project]/[app]/src/main/jni/myNDKModule.cpp
stl值应设置为&#34; stlport_shared&#34;或&#34; stlport_static&#34;如果你想使用stlport库的C ++。如果您不需要扩展的C ++支持,可以退出stl。记住Android默认情况下提供非常基本的C ++支持。对于其他受支持的C ++库,请在您下载的NDK中查看NDK文档指南。请注意,通过在此处将其设置为stlport_shared,gradle将libstlport_shared.so lib从NDK的sources / cxx-stl / stlport / libs目录复制到APK的lib目录。它还处理编译器中的包含路径(从技术上讲,gradle并不是完成所有这些,而是Android NDK构建系统)。因此,不要将自己的stlport副本放入jniLibs目录。
最后,我认为cFlags非常明显。
你不能在Mac OSX上设置ANDROID_NDK_HOME(见下文),但是从我做过的一些研究中看来,这可能仍适用于其他操作系统。它将被删除。
我想发表评论但尚未获得声誉。丹尼斯,环境变量被完全忽略,而不仅仅是被覆盖。实际上,您没有获得任何环境变量。据我所知,Android Studio IDE只用几个特定的环境变量创建自己的环境(检查System.getenv()并从gradle脚本中打印出来)。
我在这里写了一个错误,因为使用env vars从cmd行建立得很好:
https://code.google.com/p/android/issues/detail?id=65213
但正如您所看到的,Google决定他们根本不想让IDE使用环境变量;我仍然坚持这个决定。让我的生活变得痛苦,我必须更新local.properties以指向可以在我的gradle脚本中加载的绝对路径,我仍然没有想出怎么做(但是看起来很难看)。这意味着我要么强迫我的团队成员使用与我相同的路径,玩链接,让他们在每次拉回购物时都输入它们,或添加自动化脚本。我认为这是一个糟糕的决定,任何依赖于env vars的开发人员都会花费时间,这些开发人员在微观层面可能很小,但在宏观层面却很大。
groundloop,我相信IDE很快就会更新,并能够将NDK文件夹路径添加到项目中,并且它会自动生成local.properties文件(至少它不会有意义)他们没有想到这一点。)
有关Google的更多详细示例,以下是最新示例(搜索jni或ndk): https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA
使用NDK的跨平台胖APK:
最后,使用gradle并且无法提供自己的Android.mk文件存在缺陷,因此您只能将第三方本机库从单个体系结构链接到NDK。注意我说&#34;链接在&#34;。您可以使用&#34; abiFilters&#34;在多个体系结构中构建NDK模块(上面的moduleName)。命令,它们将被放置在您的应用程序中,以便可以在多个体系结构上使用相同的APK。如果你需要链接你自己的第三方库,或者根据你的架构有不同的cFlags值,那么这不是一个简单的方法。
我尝试了下面的内容,它起初似乎起作用了,但后来我发现它只是通过将两个ndk部分中的所有内容相加来构建NDK(或类似的东西,它以某种方式构建多个架构库虽然):
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和本地第三方库创建一个干净的庄园中的胖二进制之后,我终于得出结论,谷歌Play内置的多架构支持APK真的是最好的无论如何要去的路线,所以为每个架构创建单独的APK。
所以我创建了多个buildTypes,没有产品风格,并添加了以下代码来生成每种类型的版本代码。
// 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的值,就像您通常那样,并根据构建类型将其附加到特定于体系结构的版本字符串的末尾。版本字符串对于所有版本保持不变,但会改变代码以提供从ARM一直到X86_64的优先顺序。它有点hackish或硬编码,但它完成了工作。请注意,这为您提供了多达999个版本,因此如果您需要更多版本,请将上面的数字乘以10,不确定您可以为版本代码添加的最大值。
就我而言,我们有一个相当复杂的构建系统。我们为9个架构构建了CPython,其中3个是Android,然后构建了一堆我们自己的库,并将它们全部链接到每个架构的单个库中。我们使用ndk命令行构建工具,automake和python来构建所有内容,而不是Android.mk文件。然后将最终的库链接到单个JNI接口cpp文件(上面称为myNativeCPPModule)。只需单击一下按钮,一切都可以立即构建,非常漂亮的Android Studio。
答案 1 :(得分:34)
找到答案。在ndk.dir=path/to/ndk
文件中加入local.properties
就可以了。
<强>更新强> 在最新版本的Android Studio上,您可以直接在项目结构&gt;中设置值。 SDK位置。
答案 2 :(得分:15)
您还可以设置ANDROID_NDK_HOME环境变量
答案 3 :(得分:1)
我花了很多时间在build.gradle中配置ndk。我得到good blog解决了我的问题。
答案 4 :(得分:0)
如前所述,在local.properties中添加ndk.dir =会有所帮助。有趣的是,我发现local.properties会覆盖为环境变量ANDROID_NDK_HOME,设置的任何值,即使您没有在local.properties中配置ndk.dir 。 (至少使用gradle android插件v 0.7.3)。
这是令人困惑的,因为Android Studio可以覆盖local.properties,它似乎没有提供配置ndk.dir的方法:(
答案 5 :(得分:0)
Android工作室建议在local.properties中包含ndk的路径