我对整个gradle和Android Studio支持都是新手。我设法使用export选项将我的android项目转换为gradle。
但是我正在寻找一些文档或起点,以了解如何将NDK构建集成到gradle构建过程中。
如果可能的话,我还需要某种“之后”阶段,将构建二进制文件(.so文件)复制到资产目录。
我对整个gradle和Android Studio支持都是新手。我设法使用export选项将我的android项目转换为gradle。
但是我正在寻找一些文档或起点,以了解如何将NDK构建集成到gradle构建过程中。
如果可能的话,我还需要某种“之后”阶段,将构建二进制文件(.so文件)复制到资产目录。
Answers:
我们已经在1.3版中发布了集成的第一个版本作为预览:http : //tools.android.com/tech-docs/android-ndk-preview
即使在1.3成为最终版本后,集成仍将保留预览。目前尚无最终确定时间(截至2015/07/10)。
此处提供更多信息:http : //tools.android.com/tech-docs/android-ndk-preview
更新:支持NDK的Android Studio现已推出:http : //tools.android.com/tech-docs/android-ndk-preview
为了使用脚本构建,下面的gradle解决方案应该可以工作:
我正在使用自己的构建脚本并将其添加到我的文件中(似乎适用于0.8+
):这似乎等效于以下解决方案(但在gradle文件中看起来更好):
android {
sourceSets {
main {
jniLibs.srcDirs = ['native-libs']
jni.srcDirs = [] //disable automatic ndk-build
}
}
}
不幸的是,如果目录不存在或不包含.so
文件,构建不会失败。
tasks.withType(com.android.build.gradle.tasks.PackageApplication) { it.jniFolders = [file("libs")] as Set }
。谢谢大家的帮助!
jni.srcDirs = [] //disable automatic ndk-build
非常重要,因为它将阻止Android Studio重建C / C ++源代码。我一直在努力解决这一问题,直到我看到您的帖子为止,这解决了我的问题。我真的认为NDK构建最好仅由Android.mk命令行makefile单独构建,而不是由gradle脚本单独构建,因为C / C ++由Makefile构建已有40多年了!
随着Android Studio更新至1.0,NDK工具链支持得到了极大的改善(注意:请阅读本文底部的更新,以查看新的实验性Gradle插件和Android Studio 1.5的用法)。
Android Studio和NDK已很好地集成在一起,因此您只需要在模块的build.gradle中创建一个ndk {}块,然后将源文件设置到(module)/ src / main / jni目录中即可,完成!
从命令行不再需要ndk-build。
我已经在我的博客文章中写过所有这些文章:http : //www.sureshjoshi.com/mobile/android-ndk-in-android-studio-with-swig/
重点是:
您需要在这里了解两件事。默认情况下,如果您有要加载到Android应用程序中的外部库,则默认情况下会在(module)/ 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 (i.e. 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 ++代码的过程,您需要从那里加载它,然后创建包装器-但是从您的问题来看,您已经知道如何做所有这些事情,因此,我不再赘述。
另外,我在这里放置了这个示例的Github存储库:http : //github.com/sureshjoshi/android-ndk-swig-example
当Android Studio 1.3发布时,应该通过JetBrains CLion插件更好地支持C ++。我目前的假设是,这将允许从Android Studio中进行Java和C ++开发。但是我认为我们仍然需要使用如上所述的Gradle NDK部分。此外,我认为仍然需要编写Java <-> C ++包装器文件,除非CLion将自动执行这些文件。
我已经更新了我的博客和Github存储库(在development分支中),以将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构建如下所示:
android.ndk {
moduleName = "SeePlusPlus" // Name of C++ module (i.e. 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使用'native'关键字可以自动完成C ++-Java生成的包装器:
但是,这并不完全乐观……如果您使用SWIG来包装库以自动生成代码,然后尝试使用本机关键字自动生成,它将把代码放在Swig _wrap中的错误位置.cxx文件...因此,您需要将其移动到“ extern C”块中:
如果我不提Android Studio 2.2及更高版本通过Gradle和CMake对NDK工具链的本质上是“本地”(无双关)的支持,那我将不为所动。现在,当您创建一个新项目时,只需选择C ++支持就可以了。
您仍然需要生成自己的JNI层代码,或者使用我上面提到的SWIG技术,但是Android项目中C ++的脚手架现在变得微不足道了。
CMakeLists文件(在其中放置C ++源文件)中的更改将由Android Studio接收,它将自动重新编译任何关联的库。
现在它已经无法预览,并且对所有人都可用:https : //developer.android.com/studio/projects/add-native-code.html
ndk-build
如果jni
项目源中有目录,Gradle会自动调用。这适用于Android Studio 0.5.9(加纳利版)。
添加ANDROID_NDK_HOME
到您的环境变量中或添加ndk.dir=/path/to/ndk
到您local.properties
的Android Studio项目中。这使Android Studio可以自动运行ndk。
下载最新的gradle示例项目,以查看ndk项目的示例。(它们在页面的底部)。一个好的示例项目是ndkJniLib
。
gradle.build
从NDK示例项目复制。它看起来像这样。这gradle.build
会为每种架构创建不同的apk。您必须使用build variants
窗格选择所需的体系结构。
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禁用自动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构建脚本中显式调用ndk-build,因为您只是禁用了自动调用。
task ndkBuild(type: Exec) {
commandLine 'ndk-build', '-C', file('src/main/jni').absolutePath
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
tasks.all { task -> if (task.name.contains('Ndk')) task.enabled = false }
正如Xavier所说,如果使用gradle 0.7.2+,您可以将预构建的文件放在/ src / main / jniLibs /中
摘自:https : //groups.google.com/d/msg/adt-dev/nQobKd2Gl_8/ctDp9viWaxoJ
到目前为止(Android Studio v0.8.6),它非常简单。以下是创建“ Hello world”类型应用的步骤:
下载Android NDK并将根文件夹放在合理的位置-也许与SDK文件夹位于同一位置。
将以下内容添加到您的local.properties
文件中:
ndk.dir=<path-to-ndk>
将以下内容添加到defaultConfig
闭包内部的build.gradle文件中,紧接在该versionName
行之后:ndk { moduleName="hello-world" }
在您应用模块的main
目录中,创建一个名为的新文件夹jni
。
在该文件夹中,创建一个名为的文件hello-world.c
,您将在下面看到它。
有关Activity
如何在中调用方法(或它是函数?)的示例,请参见下面的示例代码hello-world.c
。
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'])
}
Java_me_mattlogan_ndktest_MainActivity_stringFromJNI
为自己的:)
如果您使用的是UNIX,则最新版本(0.8)将添加ndk-build。新增方法如下:
android.ndk {
moduleName "libraw"
}
它期望在“ src / main / jni”下找到JNI,否则可以使用以下命令进行定义:
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中显示了一种优雅的解决方法。
基本上,您创建一个包含“ lib / armeabi / yourlib.so”的jar,然后将其包含在构建中。
ndk-build
,.jar
为每个脚本生成s,.so
并将它们放在gradle的构建路径中以减轻这种痛苦。看看这个。
.so
在另一个(封闭的)线程中给出了一个很好的答案,可以自动打包易于编译的文件。为了使它正常工作,我必须更改行:
from fileTree(dir: 'libs', include: '**/*.so')
变成:
from fileTree(dir: 'src/main/libs', include: '**/*.so')
没有此更改,将.so
找不到文件,因此打包文件的任务将永远无法运行。
@plaisthos的答案出现在最新的gradle版本中,但是仍然有一种方法。native-libs
在项目目录的根目录中创建一个目录,然后将所有库复制到该目录中。
将以下行添加到build.gradle中。建立并快乐。
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构建的代码。为此,在gradle.properties
ie中添加ndk目录路径。从下面发布的代码中可以看到ndkdir=/home/user/android-ndk-r9d
,将所有jni文件添加并放入一个文件夹native
中src/main/
。它将使用本地库创建jar,您可以正常使用它们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'
我使用以下代码编译本机保管箱库,我使用的是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
NDK的//路径
在local.properties在Android Studio中的项目文件。:与加入这一行
android.useDeprecatedNdk=true
中gradle.properties在Android Studio中的项目文件。
此处的更多信息:http : //tools.android.com/tech-docs/android-ndk-preview
为了扩展Naxos所说的内容(感谢Naxos向正确的方向发送消息!),我从最近发布的NDK示例中学到了很多,并在此处发布了类似问题的答案。
这篇文章详细介绍了如何将预构建的本机库链接到各种架构的应用程序中,以及有关如何将NDK支持直接添加到build.gradle脚本中的信息。在大多数情况下,您无需再进行zip和复制工作。
这是我用来使NDK在Android Studio项目中工作的步骤。我使用本教程来帮助我 https://software.intel.com/zh-cn/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允许我登录到LogCat,stl是要导入的stl。与Eclipse NDK一样,有很多选项。(http://www.kandroid.org/ndk/docs/CPLUSPLUS-SUPPORT.html)
cFlags对我来说仍然是一定数量的黑魔法。对于所有选项及其给我的东西,我都找不到很好的信息来源。在StackOverflow周围搜索所需的任何东西,这就是我找到它的地方。我确实知道c ++ 11允许我使用新的c ++ 11标准。
这是我如何从本机代码登录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());
从eclipse 在android studio中配置项目:您必须将eclipse ndk项目导入到android studio中而不导出到gradle,并且它可以正常工作,还需要在local.properties中添加ndk的路径,如果显示错误然后添加
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] //disable automatic ndk-build callenter code here
}
在build.gradle文件中,然后使用终端创建jni文件夹和文件并运行它即可
现在,Android Studio处于稳定状态,运行android-ndk示例非常简单。这些示例使用ndk实验性插件,并且比Android NDK在线文档中链接的示例更新。一旦知道它们可以工作,就可以研究build.gradle,local.properties和gradle-wrapper.properties文件,并相应地修改项目。以下是使它们工作的步骤。
转到“设置”,“外观和行为”,“系统设置”,“ Android SDK”,选择“ SDK工具”选项卡,然后在列表底部检查“ Android NDK版本1.0.0”。这将下载NDK。
指向新下载的NDK的位置。请注意,它将放置在sdk / ndk-bundle目录中。通过选择文件,项目结构,SDK位置(在左侧),并在Android NDK位置下提供路径来执行此操作。这会将ndk条目添加到local.properties中,如下所示:
Mac / Linux:ndk.dir = / Android / sdk / ndk-bundle
Windows:ndk.dir = C:\ Android \ sdk \ ndk-bundle
除了gles3gni,本机编解码器和生成器之外,我已经通过这种方式成功构建并部署了存储库中的所有项目。我正在使用以下内容:
Android Studio 1.3 build AI-141.2117773
android-ndk示例发布于2015年7月28日(上面的链接)
SDK工具24.3.3
NDK r10e提取到C:\ Android \ sdk \ ndk-bundle
Gradle 2.5
Gradle插件0.2.0
Windows 8.1 64位
通常,使用NDK进行构建就像正确地指定到Android.mk的ndkBuild路径或CMakeLists.txt的cmake路径一样简单。我建议在较旧的Android.mk上使用CMake,因为Android Studio的C / C ++支持基于CLion,并且它使用CMake作为其项目格式。以我的经验,这倾向于使IDE对大型项目的响应速度更快。您项目中编译的所有内容都会自动生成并复制到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'])
}
NDK构建中的静态库(.a)将自动包括在内,但预构建的动态库(.so)将需要放置在中jniLibs
。可以使用sourceSets
,但是您应该采用标准。build.gradle
包括预构建的库时,不需要任何其他命令。
jniLibs
您可以在《Android Gradle插件用户指南》中找到有关该结构的更多信息。
|--app: |--|--build.gradle |--|--src: |--|--|--main |--|--|--|--java |--|--|--|--jni |--|--|--|--|--CMakeLists.txt |--|--|--|--jniLibs |--|--|--|--|--armeabi |--|--|--|--|--|--.so Files |--|--|--|--|--armeabi-v7a |--|--|--|--|--|--.so Files |--|--|--|--|--x86 |--|--|--|--|--|--.so Files
然后,您可以验证生成的APK是否包含您的.so文件,通常位于下build/outputs/apk/
,unzip -l myApp.apk
用于列出内容。
如果要在NDK中构建共享库,则无需执行任何其他操作。它将正确捆绑在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文件添加到此路径
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");
尽管我相信SJoshi(Oracle专家)能给出最完整的答案,但SWIG项目是一个特殊的案例,既有趣又有用,但是对于大多数使用基于SDK ant的标准项目表现出色的项目而言,SWIG项目并没有得到推广。 NDK。我们所有人都希望现在最有可能使用Android Studio,或者希望有一个更加CI友好的移动版构建工具链,从理论上讲,这是可以提供的。
我已经发布了我的方法,它是从某个地方借来的(我在SO上找到了它,但是发布了app build.gradle的要点:https ://gist.github.com/truedat101/c45ff2b69e91d5c8e9c7962d4b96e841 )。简而言之,我建议以下内容:
在我看来,适用于Android的Gradle真是一团糟,就像我喜欢借用的Maven概念以及专为项目设计的目录结构一样。NDK的这一功能已经“推出”了将近3年了。