Android工作室,Gradle和NDK
我对这个完整的gradle和Android Studio支持非常新颖。 我设法将我的Android项目转换为使用导出选项的Gradle。
但是我正在寻找一些文档或开始如何将NDK构build集成到gradle构build过程中。
如果可能的话,我还需要某种“后”阶段,将构build二进制文件(.so文件)复制到资产目录。
我们在1.3版本中发布了第一个集成版本: http : //tools.android.com/tech-docs/android-ndk-preview
即使1.3成为最终版本,整合也将保持预览。 到目前为止,还没有ETA(截至2015年7月10日)。
更多信息在这里: http : //tools.android.com/tech-docs/android-ndk-preview
更新:与NDK支持的Android Studio现在是: http : //tools.android.com/tech-docs/android-ndk-preview
用脚本构build下面的gradle解决scheme应该是可行的:
我正在使用我的构build脚本,并添加到我的文件(似乎工作0.8+
):这似乎相当于下面的解决scheme(但看起来更好的gradle文件):
android { sourceSets { main { jniLibs.srcDirs = ['native-libs'] jni.srcDirs = [] //disable automatic ndk-build } } }
不幸的是,如果目录不存在或不包含任何.so
文件,则内部版本不会失败。
随着Android Studio的更新到1.0,NDK工具链支持得到了极大的改善( 注意:请阅读我的更新在这篇文章的底部看到使用新的实验Gradle插件和Android Studio 1.5 )。
Android Studio和NDK的集成已经足够好了,所以你只需要在你的模块的build.gradle中创build一个ndk {}模块,并将你的源文件设置到(module)/ src / main / jni目录中,完成了!
没有更多的ndk生成从命令行。
我已经在我的博客文章写在这里: http : //www.sureshjoshi.com/mobile/android-ndk-in-android-studio-with-swig/
要点是:
这里有两件事你需要知道。 默认情况下,如果您有要加载到Android应用程序的外部库,默认情况下会在(模块)/ src / main / jniLibs中查找它们。 你可以通过在模块的build.gradle中设置sourceSets.main.jniLibs.srcDirs来改变它。 你将需要一个子目录,每个架构的目标库(例如,x86,arm,mips,arm64-v8a等)
NDK工具链默认编译的代码将位于(module)/ src / main / jni中,与上面类似,您可以通过在模块的build.gradle中设置sourceSets.main.jni.srcDirs来更改它
并把它放到你的模块的build.gradle中:
ndk { moduleName "SeePlusPlus" // Name of C++ module (ie libSeePlusPlus) cFlags "-std=c++11 -fexceptions" // Add provisions to allow C++11 functionality stl "gnustl_shared" // Which STL library to use: gnustl or stlport }
这是编译C ++代码的过程,从那里你需要加载它,并创build包装 – 但从你的问题来看,你已经知道如何做,所以我不会重新哈希。
另外,我在这里放置了这个例子的Github仓库: http : //github.com/sureshjoshi/android-ndk-swig-example
更新:2015年6月14日
当Android Studio 1.3发布时,通过JetBrains CLion插件应该可以更好地支持C ++。 我目前正在假设这将允许Android Studio中的Java和C ++开发; 但是我想我们仍然需要使用Gradle NDK部分,正如我上面所说的那样。 此外,我认为仍然需要编写Java < – > C ++包装文件,除非CLion会自动执行这些文件。
更新:2016年1月5日
我已经更新了我的博客和Github回购(在开发分支)使用Android Studio 1.5和最新的实验Gradle插件(0.6.0-alpha3)。
http://www.sureshjoshi.com/mobile/android-ndk-in-android-studio-with-swig/ http://github.com/sureshjoshi/android-ndk-swig-example
现在NDK部分的Gradle构build如下所示:
android.ndk { moduleName = "SeePlusPlus" // Name of C++ module (ie libSeePlusPlus) cppFlags.add("-std=c++11") // Add provisions to allow C++11 functionality cppFlags.add("-fexceptions") stl = "gnustl_shared" // Which STL library to use: gnustl or stlport }
另外,非常棒的是,Android Studio为C ++自动完成 – Java生成的使用“native”关键字的包装器:
然而,这并不是完全乐观的…如果你使用SWIG来包装一个库来自动生成代码,然后尝试使用native关键字自动生成,它会把代码放在你的Swig _wrap中的错误位置.cxx文件…所以你需要把它移动到“extern C”块中:
更新:2017年10月15日
如果我没有提到Android Studio 2.2开始通过Gradle和CMake实现对NDK工具链的本地支持(无双关语言)支持,我会感到失望。 现在,当你创build一个新项目时,只需selectC ++支持,你就可以开始。
您仍然需要生成自己的JNI图层代码,或者使用上面提到的SWIG技术,但Android项目中的C ++脚手架现在是微不足道的。
CMakeLists文件(放置C ++源文件的位置)中的更改将被Android Studio拾取,并且会自动重新编译任何关联的库。
在Google IO 2015中,Google宣布了Android Studio 1.3中的完整NDK集成。
现在已经没有预览,并且提供给所有人: https : //developer.android.com/studio/projects/add-native-code.html
老的回答:如果您的项目源中有jni
目录,Gradle会自动调用ndk-build
。
这是工作在Android Studio 0.5.9(金丝雀build设)。
- 下载NDK
- 将
ANDROID_NDK_HOME
添加到您的环境variables,或将ndk.dir=/path/to/ndk
到您的Android Studio项目的local.properties
中。 这允许Android studio自动运行ndk。 - 下载最新的gradle示例项目来查看ndk项目的示例。 (他们在页面的底部)。 一个好的示例项目是
ndkJniLib
。 -
从NDK示例项目中复制
gradle.build
。 它会看起来像这样。 这个gradle.build
为每个架构创build一个不同的apk。 您必须使用build variants
窗格来select您想要的体系结构。apply plugin: 'android' dependencies { compile project(':lib') } android { compileSdkVersion 19 buildToolsVersion "19.0.2" // This actual the app version code. Giving ourselves 100,000 values [0, 99999] defaultConfig.versionCode = 123 flavorDimensions "api", "abi" productFlavors { gingerbread { flavorDimension "api" minSdkVersion 10 versionCode = 1 } icecreamSandwich { flavorDimension "api" minSdkVersion 14 versionCode = 2 } x86 { flavorDimension "abi" ndk { abiFilter "x86" } // this is the flavor part of the version code. // It must be higher than the arm one for devices supporting // both, as x86 is preferred. versionCode = 3 } arm { flavorDimension "abi" ndk { abiFilter "armeabi-v7a" } versionCode = 2 } mips { flavorDimension "abi" ndk { abiFilter "mips" } versionCode = 1 } fat { flavorDimension "abi" // fat binary, lowest version code to be // the last option versionCode = 0 } } // make per-variant version code applicationVariants.all { variant -> // get the version code of each flavor def apiVersion = variant.productFlavors.get(0).versionCode def abiVersion = variant.productFlavors.get(1).versionCode // set the composite code variant.mergedFlavor.versionCode = apiVersion * 1000000 + abiVersion * 100000 + defaultConfig.versionCode } }
请注意,这将忽略您的Android.mk和Application.mk文件。 作为解决方法,您可以告诉gradle禁用atuomatic ndk-build调用,然后手动指定ndk源的目录。
sourceSets.main { jniLibs.srcDir 'src/main/libs' // use the jni .so compiled from the manual ndk-build command jni.srcDirs = [] //disable automatic ndk-build call }
另外,你可能会明确地在你的gradle构build脚本中调用ndk-build,因为你只是禁用了自动调用。
task ndkBuild(type: Exec) { commandLine 'ndk-build', '-C', file('src/main/jni').absolutePath } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn ndkBuild }
我发现“gradle 1.11 com.android.tools.build:gradle:0.9.+”现在支持预编译ndk,你可以把* .so放在dir src / main / jniLibs中。 当build立gradle将打包ndk到正确的地方。
这是我的项目
项目: | --src | - | - 主 | - | - | --java | - | - | --jniLibs | - | - | - | --armeabi | - | - | - | - | - so文件 | --libs | - | --other.jar
正如Xavier所说,如果您使用gradle 0.7.2+版本,您可以将预编译器放在/ src / main / jniLibs /
取自: https : //groups.google.com/d/msg/adt-dev/nQobKd2Gl_8/ctDp9viWaxoJ
截至目前(Android Studio v0.8.6),它非常简单。 以下是创build“Hello world”types应用程序的步骤:
-
下载Android NDK,并把根文件夹放在一个理智的地方 – 也许在SDK文件夹的相同位置。
-
将以下内容添加到
local.properties
文件中:ndk.dir=<path-to-ndk>
-
将下面的内容添加到
defaultConfig
闭包中的build.gradle文件中,在versionName
行之后:ndk { moduleName="hello-world" }
-
在您的应用程序模块的
main
目录中,创build一个名为jni
的新文件夹。 -
在该文件夹中,创build一个名为
hello-world.c
的文件,您将在下面看到这个文件。 -
有关如何在
hello-world.c
调用方法(或函数?)的示例,请参阅下面的Activity
代码示例。
hello-world.c
#include <string.h> #include <jni.h> jstring Java_me_mattlogan_ndktest_MainActivity_stringFromJNI(JNIEnv* env, jobject thiz) { return (*env)->NewStringUTF(env, "Hello world!"); }
MainActivity.java
public class MainActivity extends Activity { static { System.loadLibrary("hello-world"); } public native String stringFromJNI(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String testString = stringFromJNI(); TextView mainText = (TextView) findViewById(R.id.main_text); mainText.setText(testString); } }
build.gradle
apply plugin: 'com.android.application' android { compileSdkVersion 20 buildToolsVersion "20.0.0" defaultConfig { applicationId "me.mattlogan.ndktest" minSdkVersion 15 targetSdkVersion 20 versionCode 1 versionName "1.0" ndk { moduleName "hello-world" } } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) }
在这里find一个非常类似的应用程序的完整源代码(减去NDK)。
如果你使用unix,最新版本(0.8)增加了ndk-build。 以下是如何添加它:
android.ndk { moduleName "libraw" }
它期望在'src / main / jni'下findJNI,否则你可以用下面的方法定义它:
sourceSets.main { jni.srcDirs = 'path' }
截至2014年1月28日,版本为0.8的版本在Windows上打破,您必须禁用该版本:
sourceSets.main { jni.srcDirs = [] //disable automatic ndk-build call (currently broken for windows) }
https://groups.google.com/d/msg/adt-dev/nQobKd2Gl_8/Z5yWAvCh4h4J中显示了一个优雅的解决方法。;
基本上你创build一个包含“lib / armeabi / yourlib.so”的jar,然后在构build中包含jar。
在另一个(封闭的)线程中给出一个很好的答案来自动化易于编译的.so
文件的打包。 要做到这一点,我必须改变这一行:
from fileTree(dir: 'libs', include: '**/*.so')
成:
from fileTree(dir: 'src/main/libs', include: '**/*.so')
没有这个改变, .so
文件没有find,因此打包它们的任务将永远不会运行。
@plaisthos的答案在最新的gradle版本中打破了,但仍然有办法做到这一点。 在项目目录的根目录下创build一个native-libs
目录,并将所有的lib拷贝到这个目录中。
将以下行添加到您的build.gradle。 build立和快乐。
task copyNativeLibs(type: Copy) { from(new File(project(':<your project>').getProjectDir(), 'native-libs')) { include '**/*.so' } into new File(buildDir, 'native-libs') } tasks.withType(Compile) { compileTask -> compileTask.dependsOn copyNativeLibs } clean.dependsOn 'cleanCopyNativeLibs'
这是我使用从gradle使用android-ndk构build的代码。 为此,在gradle.properties
添加ndk目录path。 添加ndkdir=/home/user/android-ndk-r9d
,并将所有jni文件放在src/main/
中的native
文件夹中,如下面的代码所示。 它将创build与本地库,您可以正常使用的System.loadLibrary("libraryname");
dependencies { compile fileTree(dir: "$buildDir/native-libs", include: '*.jar') } task ndkBuild(type: Exec) { commandLine "$ndkdir/ndk-build", "--directory", "$projectDir/src/main/native", '-j', Runtime.runtime.availableProcessors(), "APP_PLATFORM=android-8", "APP_BUILD_SCRIPT=$projectDir/src/main/native/Android.mk", "NDK_OUT=$buildDir/native/obj", "NDK_APP_DST_DIR=$buildDir/native/libs/\$(TARGET_ARCH_ABI)" } task nativeLibsToJar(type: Jar, description: 'create a jar with native libs') { destinationDir file("$buildDir/native-libs") baseName 'native-libs' from fileTree(dir: "$buildDir/native/libs", include: '**/*.so') into 'lib/' } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn nativeLibsToJar } nativeLibsToJar.dependsOn 'ndkBuild'
我已经使用下面的代码来编译本机Dropbox库,我正在使用Android Studio v1.1。
task nativeLibsToJar(type: Zip) { destinationDir file("$buildDir/native-libs") baseName 'native-libs' extension 'jar' from fileTree(dir: 'src/main/libs', include: '**/*.so') into 'lib/' } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn(nativeLibsToJar) }
我用ndk.dir=/usr/shareData/android-ndk-r11b
// ndkpath
在android studio项目中的local.properties文件中。 并添加这一行:
android.useDeprecatedNdk=true
在android studio项目中的gradle.properties文件中。
更多信息在这里: http : //tools.android.com/tech-docs/android-ndk-preview
为了扩大纳克索斯所说的(感谢纳克索斯让我朝着正确的方向发展!),我从最近公布的NDK例子中学到了很多东西,并在这里发表了一个类似问题的答案。
如何用Android Gradle插件0.7configurationNDK
本文详细介绍了如何将预build的本地库链接到各种体系结构的应用程序,以及如何将NDK支持直接添加到build.gradle脚本的信息。 在大多数情况下,你不应该需要围绕邮编和复制工作了。
以下是我用于在Android Studio项目中获取NDK的步骤。 我使用这个教程来帮助我https://software.intel.com/en-us/videos/using-the-ndk-with-android-studio
为了使用NDK,你必须添加一个NDK行到local.properties。 所以在你的sdk.dir下添加
ndk.dir=C\:\\MyPathToMyNDK\ndk
在我的应用程序build.gradle我有以下代码
ndk { moduleName "myLib" ldLibs "log" stl "gnustl_shared" cFlags "-std=c++11 -frtti -fexceptions -pthread" }
moduleName是你想给你的本地代码的名字。 我相信这是共享库将被称为。 ldLibs允许我login到LogCat,stl是你想要导入的stl。 有很多选项,与Eclipse NDK相同。 ( http://www.kandroid.org/ndk/docs/CPLUSPLUS-SUPPORT.html )
cFlags对我来说还是一定的黑魔法。 我还没有find所有选项和他们给我的一个很好的来源。 在StackOverflow中search任何你需要的东西,那就是我find它的地方。 我知道c ++ 11允许我使用新的c ++ 11标准。
这里是我如何从本地代码login到LogCat的例子
__android_log_print(ANDROID_LOG_DEBUG, "TestApp", "Adding - String %d has a field name of %s and a value of %s", i, lKeyUTF8.c_str(), lValueUTF8.c_str());
从eclipseconfiguration项目在android工作室 :你必须导入eclipse ndk项目到android studio没有导出到gradle,它的工作原理,也需要添加path的ndk在local.properties ,如果显示错误,然后添加
sourceSets.main { jniLibs.srcDir 'src/main/libs' jni.srcDirs = [] //disable automatic ndk-build callenter code here }
在build.gradle文件,然后使用terminal创buildjni文件夹和文件,运行它将工作
现在Android Studio处于稳定的频道,运行android-ndk样本非常简单。 这些示例使用ndk实验性插件 ,并且比从Android NDK联机文档链接到的更新。 一旦你知道他们的工作,你可以研究build.gradle,local.properties和gradle-wrapper.properties文件,并相应地修改你的项目。 以下是让他们工作的步骤。
-
转到设置,外观和行为,系统设置,Android SDK,selectSDK Tools标签,并检查列表底部的Android NDK版本1.0.0。 这将下载NDK。
-
指向新下载的NDK的位置。 请注意,它将被放置在sdk / ndk-bundle目录中。 通过select文件,项目结构,SDK位置(左侧)以及在Android NDK位置下提供path来完成此操作。 这将添加一个ndk条目到local.properties类似于这样的:
Mac / Linux:ndk.dir = / Android / sdk / ndk-bundle
Windows:ndk.dir = C:\ Android \ sdk \ ndk-bundle
我已经用这种方法成功构build和部署了存储库中的所有项目,除了gles3gni,native-codec和builder。 我正在使用以下内容:
Android Studio 1.3 build AI-141.2117773
2015年7月28日发布的android-ndk样本(上面的链接)
SDK工具24.3.3
NDK r10e解压缩到C:\ Android \ sdk \ ndk-bundle
Gradle 2.5
Gradle插件0.2.0
Windows 8.1 64位
NDK构build和gradle(基本)
通常使用NDK构build和正确指定Android.mk的ndkBuildpath或CMakeLists.txt的cmakepath一样简单。 我推荐在早期的Android.mk上使用CMake,因为Android Studio的C / C ++支持基于CLion,它使用CMake作为它的项目格式。 这在我的经验中倾向于使IDE在大型项目上更有响应。 在您的项目中编译的所有东西都将被自动构build并复制到APK中。
apply plugin: 'com.android.library' android { compileSdkVersion 19 buildToolsVersion "25.0.2" defaultConfig { minSdkVersion 19 targetSdkVersion 19 ndk { abiFilters 'armeabi', 'armeabi-v7a', 'x86' // 64-bit support requires an Android API level higher than 19; Namely 21 and higher //abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } externalNativeBuild { cmake { arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_PLATFORM=android-19', '-DANDROID_STL=gnustl_static', '-DANDROID_ARM_NEON=TRUE' } } } externalNativeBuild { cmake { path 'src/main/jni/CMakeLists.txt' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) }
将预build库添加到项目中(高级)
NDK构build中的静态库(.a)将自动包含在内,但是需要将预先构build的dynamic库(.so)放置在jniLibs
。 这可以使用sourceSets
进行configuration,但是您应该采用标准。 包含预build库时,您build.gradle
任何其他命令。
jniLibs
的布局
您可以在Android Gradle插件用户指南中find关于结构的更多信息。
|--app: |--|--build.gradle |--|--src: |--|--|--main |--|--|--|--java |--|--|--|--jni |--|--|--|--|--CMakeLists.txt |--|--|--|--jniLibs |--|--|--|--|--armeabi |--|--|--|--|--|--.so Files |--|--|--|--|--armeabi-v7a |--|--|--|--|--|--.so Files |--|--|--|--|--x86 |--|--|--|--|--|--.so Files
然后,您可以validation生成的APK包含您的.so文件,通常在build/outputs/apk/
,使用unzip -l myApp.apk
列出内容。
build立共享库
如果您在NDK中构build共享库,则不需要进一步操作。 它将被正确捆绑在APK中。
只需将这些行添加到应用程序build.gradle
dependencies { ... compile fileTree(dir: "$buildDir/native-libs", include: 'native-libs.jar') } task nativeLibsToJar(type: Zip, description: 'create a jar archive of the native libs') { destinationDir file("$buildDir/native-libs") baseName 'native-libs' extension 'jar' from fileTree(dir: 'libs', include: '**/*.so') into 'lib/armeabi/' } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn(nativeLibsToJar) }
现在我可以加载这么成功!
1.将.so文件添加到此path
Project:
| –src | – | –main | – | – | –java | – | – | –jniLibs | – | – | – | –armeabi | – | – | – | – | – so文件
2.将此代码添加到gradle.build
android { splits { abi { enable true reset() include 'x86', 'x86_64', 'arm64-v8a', 'armeabi-v7a', 'armeabi' universalApk false } }
}
3. System.loadLibrary("yousoname");
- goodluck给你,这与gradle 1.2.3没关系
-
如果您的项目从eclipse导出,请在gradle文件中添加以下代码:
android { sourceSets{ main{ jniLibs.srcDir['libs'] } } }
2.如果你在Android studio中创build一个项目:
在src / main /中创build一个名为jniLibs的文件夹,并将* .so文件放在jniLibs文件夹中。
并在您的gradle文件中复制代码如下:
android { sourceSets{ main{ jniLibs.srcDir['jniLibs'] } } }
虽然我相信SJOSH(甲骨文)有最完整的答案,但SWIG项目是一个特殊的情况,有趣而有用的,但对于大多数已经在标准的基于SDKant的项目+ NDK。 我们都希望现在最有可能使用Android工作室,或者希望有更多的CI友好的移动版本构build工具链,理论上可以提供这种工具链。
我已经发布了我的方法,从某处借用(我发现了这个,但贴了应用程序build.gradle的要点: https ://gist.github.com/truedat101/c45ff2b69e91d5c8e9c7962d4b96e841)。 简而言之,我build议如下:
- 不要将您的项目升级到最新的gradle版本
- 在您的项目根目录中使用com.android.tools.build:gradle:1.5.0
- 在您的应用程序项目中使用com.android.application
- 确保gradle.properties有:android.useDeprecatedNdk = true(万一抱怨)
- 使用上述方法确保您创buildAndroid.mk文件的时间和精力不会被丢弃。 您可以控制构build哪个目标拱。 这些说明对于Windows用户来说是很友好的,他们理论上应该能够在没有特殊问题的情况下在Windows上进行开发。
Android的Gradle在我看来一直是一团糟,就像我喜欢借用的Maven概念和一个项目的目录结构一样。 这NDKfunction已经“快到”近3年以上。