2013-12-19 30 views
47

新的Android Gradle插件(0.7)似乎包含对NDK的新支持,但在文档中几乎没有提及它(我发现的唯一参考是测试称为ndkSanAngeles)。如何使用Android Gradle插件配置NDK 0.7

它看起来像gradle正在寻找NDK,我已经包含在我的路径中。然而,建设项目失败与

  • 出了什么问题: 执行失败的任务 ':OGLTests:compileDefaultFlavorDebugNdk'。 NDK没有配置

我如何配置NDK的gradle中?

我目前的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' 
     } 
    } 
} 

感谢。

回答

34

找到了答案。包括ndk.dir=path/to/ndklocal.properties文件中伎俩。

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

+0

不要忘了接受你的答案 – orip

+5

是它装上去手动进'local.properties'文件一个好主意,NDK,因为它说“这个文件是由Android Studio自动生成的,不要修改这个文件 - 你的更改将被删除!” ? –

15

您还可以设置ANDROID_NDK_HOME环境变量

+0

,我已经做了,但它如何反映在应用程序?我仍然得到同样的错误。 – CoDe

+0

这不适用于最新的构建工具2.2+ –

0

正如之前的评论,加上ndk.dir =在local.properties帮助。有趣的是,我发现即使您没有在local.properties中配置ndk.dir,local.properties也会覆盖为环境变量ANDROID_NDK_HOME设置的任何值,即。 (至少使用gradle android插件v 0.7.3)。

这是混杂由于Android Studio可以覆盖local.properties,它似乎并没有提供一种方式来配置ndk.dir :(

+0

我不明白你对未指定的意见。 “local.properties中的ndk.dir会覆盖任何值......即使你没有在local.properties中配置ndk.dir”---说,什么?!?!未指定的值将覆盖环境变量....您真的说环境变量被忽略吗?或者我错过了什么? – Dennis

+0

是的,我同意@Dennis,将它从local.properties中删除是没有问题的。 – Merk

76

通过gradle这个插件代码看,我发现,帮助以下我同时使用NDK和预构建的本机库:

要简单Link在预建的本地库,只需添加一个NDK部分你的任务。例如,我在productFlavors加入它下面的abiFilter是文件夹命名库。存储在。abiFilters意味着从逗号分隔列表中的两个库将被添加到您的最终APK(所以你理论上可以有“armeabi”,“armeabi- V7A”,‘86’和‘MIPS’都在一个APK,和O/S会选择支持的体系结构LIB上安装):

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

在这个例子中,ARM生成将创建一个与APK V5和V7A arm libs,x86 build会创建一个只有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"); 
} 

您也可以放置在你的defaultConfig NDK的{}部分或buildType(如调试或释放或任何否则你可以使用)。例如:

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

通过预建的,我的意思是你下载的第三方库,或者你使用NDK工具链或自己的ARM工具链(不是NDK,构建脚本本身)建立了一个图书馆。

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


要明确构建NDK库从源 Android Studio中的当前版本,你可以做到以下几点:

设置在local.properties指向ndk.dir值如前所述的NDK之家。有谁知道你是否可以直接在local.properties中使用env vars? :)

在你的build.gradle文件,添加这样的事情你的任务(再次,可以defaultConfig,调试,发布,一个productFlavor等):

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

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

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

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

侧行一秒。该MODULENAME应该在你的项目相匹配,如JNI目录下的交流或cpp文件:

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

STL的值应设置为值“stlport_shared”或“stlport_static”如果你想使用的STLport库C++。如果你不需要扩展的C++支持,你可以离开stl。记住Android默认提供非常基本的C++支持。对于其他受支持的C++库,请在您下载的NDK中查看您的NDK文档指南。请注意,通过将其设定在这里stlport_shared,gradle这个副本从您的NDK的源/ CXX-STL/stlport的/ libs目录中的APK的lib目录中的libstlport_shared.so库。它还处理编译器中的包含路径(从技术上讲,Gradle并没有完成所有这些,但是Android NDK构建系统)。所以不要把你自己的stlport拷贝到你的jniLibs目录中。

最后,我想CFLAGS是很明显的。

你不能设置ANDROID_NDK_HOME在Mac OSX(见下文),但一些研究,我已经做了,也许这似乎仍然适用于其他操作系统。它将被删除。

我想发表评论,但没有信誉呢。丹尼斯,环境变量被完全忽略,而不仅仅是被覆盖。事实上,你没有得到任何你的环境变量。从我所知道的情况来看,Android Studio IDE通过几个特定的​​环境变量创建自己的环境(检查System.getenv()并从Gradle脚本中打印出来)。

我写这件事作为一个错误,因为在这里使用的env瓦尔建立从CMD线细:
https://code.google.com/p/android/issues/detail?id=65213

但你可以看到,谷歌决定,他们不希望在正在使用的IDE环境变量所有;我仍然在这个决定上。它让我的生活痛苦必须更新local.properties为指向我还没有想出如何做,但(但没有看过难),可以在我的脚本的gradle装载绝对路径。这意味着我要么迫使我的团队成员使用与我一样的路径,使用链接进行游戏,让他们在每次回购时都键入它们,或者添加一个自动化脚本。我认为这是一个糟糕的决定,任何依赖env变量的开发人员都需要花费时间,这些变量在微观层面可能很小,但在宏观层面却很大。

groundloop,我相信IDE将很快更新,并且能够将NDK文件夹路径添加到您的项目中,并且它会自动生成local.properties文件(至少如果它们没有意义没有想到这一点)。

对于来自谷歌更详细的例子,这里有最新的例子(搜索JNI或NDK): https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA


跨平台使用NDK脂肪的APK:

最后,有使用Gradle并且无法提供自己的Android.mk文件的缺点,因此您只能将第三方本地库从单一架构链接到NDK。注意我说“链接在”。您可以使用“abiFilters”命令在几个体系结构中构建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 
    } 
} 

现在,所有你需要做的是设置的versionCode值在defaultConfig对象,你通常会做的,这一点,追加到特定架构的版本字符串的结尾,基于构建类型。所有版本的版本字符串保持不变,但改变代码以提供从ARM到X86_64的优先顺序。这有点剽窃或硬编码,但它完成了工作。请注意,这为您提供多达999个版本,因此如果您需要更多,请将上面的数字乘以10,不知道您可以为版本代码添加的最大值是多少。

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

+0

非常有帮助。的确如此。顺便说一句,你知道用户静态库的默认搜索目录吗?如果可能,我选择不使用ldLib中的“-I”技巧。 – weidongxu

+0

如何探索/下载这里列出的文件?:https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA – Cliff

+0

找到了下载的例子,但现在我面临另一个问题w/Gradle 1.11。生成的Androud.mk文件使用绝对路径:http://stackoverflow.com/questions/23344567/ndk-dev-in-an-android-library-project-with-gradle-android-studio – Cliff

1

我花了很多时间在NDK的build.gradle配置。我得到了一个good blog解决我的问题。

0

的Android工作室建议将包括路径local.properties

相关问题