我对整个 gradle 和 Android Studio 支持非常陌生。我已经设法使用导出选项将我的 android 项目转换为 gradle。
但我正在寻找一些文档或起点,如何将 NDK 构建集成到 gradle 构建过程中。
如果可能的话,我还需要某种“之后”阶段,将构建二进制文件(.so 文件)复制到资产目录。
我对整个 gradle 和 Android Studio 支持非常陌生。我已经设法使用导出选项将我的 android 项目转换为 gradle。
但我正在寻找一些文档或起点,如何将 NDK 构建集成到 gradle 构建过程中。
如果可能的话,我还需要某种“之后”阶段,将构建二进制文件(.so 文件)复制到资产目录。
我们在 1.3 中发布了集成的第一个版本作为预览:http ://tools.android.com/tech-docs/android-ndk-preview
即使在 1.3 成为最终版本后,集成仍将保持预览状态。目前没有关于何时最终确定的预计到达时间(截至 2015 年 7 月 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
文件,则构建不会失败。
随着 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 存储库(在开发分支中)以使用带有最新实验性 Gradle 插件(0.6.0-alpha3)的 Android Studio 1.5。
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 包装一个库来自动生成代码,然后尝试使用 native 关键字自动生成,它会将代码放在你的 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
有目录,Gradle 会自动调用。jni
这适用于 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
}
我发现“gradle 1.11 com.android.tools.build:gradle:0.9.+”现在支持 pre-build ndk,你可以把 *.so 放在 dir src/main/jniLibs 中。构建 gradle 时会将 ndk 打包到正确的位置。
这是我的项目
项目: |--src |--|--主要 |--|--|--java |--|--|--jniLibs |--|--|--|--armeabi |--|--|--|--|--.so 文件 |--库 |--|--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)它非常简单。以下是创建“Hello world”类型应用程序的步骤:
下载 Android NDK 并将根文件夹放在合理的位置 - 可能与 SDK 文件夹位于同一位置。
将以下内容添加到您的local.properties
文件中:
ndk.dir=<path-to-ndk>
将以下内容添加到闭包内的 build.gradle 文件中defaultConfig
,就在该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'])
}
如果您使用的是 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,然后在构建中包含该 jar。
在另一个(关闭的)线程.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'
我使用以下代码编译原生 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)
}
这是我使用 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 项目的local.propertiesndk.dir=/usr/shareData/android-ndk-r11b
文件中使用 了 // ndk
的路径。并添加这一行:在android studio项目的gradle.properties文件中。
更多信息在这里:http ://tools.android.com/tech-docs/android-ndk-preview
android.useDeprecatedNdk=true
从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文件夹和文件并运行它将工作
以下是我用来让 NDK 在我的 Android Studio 项目中工作的步骤。我用这个教程来帮助我 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 允许我登录到 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());
为了扩展 Naxos 所说的内容(感谢 Naxos 让我朝着正确的方向前进!),我从最近发布的 NDK 示例中学到了很多东西,并在此处发布了类似问题的答案。
如何使用 Android Gradle 插件 0.7 配置 NDK
这篇文章详细介绍了如何将预构建的本地库链接到您的应用程序中以适应各种架构,以及如何将 NDK 支持直接添加到 build.gradle 脚本中。在大多数情况下,您不再需要围绕 zip 和 copy 进行工作。
现在 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 位置下提供路径来执行此操作。这将向 local.properties 添加一个 ndk 条目,类似于:
Mac/Linux:ndk.dir=/Android/sdk/ndk-bundle
Windows:ndk.dir=C:\Android\sdk\ndk-bundle
我已经以这种方式成功构建并部署了存储库中的所有项目,除了gles3gni、native-codec和builder。我正在使用以下内容:
Android Studio 1.3 构建 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 路径一样简单。我推荐 CMake 而不是较旧的 Android.mk,因为 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)
}
now.I可以加载那么成功!
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 guy) 有最完整的答案,但 SWIG 项目是一个特例,有趣且有用,但对于大多数在标准 SDK ant 基于项目中表现良好的项目并没有推广 + NDK。我们都希望现在最有可能使用 Android Studio,或者想要一个更 CI 友好的移动构建工具链,这在理论上 gradle 提供了。
我已经发布了我的方法,从某处借来的(我在 SO 上找到了这个,但发布了应用程序 build.gradle 的要点:https ://gist.github.com/trueedat101/c45ff2b69e91d5c8e9c7962d4b96e841 )。简而言之,我建议以下几点:
在我看来,Android 的 Gradle 一直是一团糟,就像我喜欢借用的 maven 概念和自以为是的项目目录结构一样。此 NDK 功能已“即将推出”近 3 年多。
如果你的项目是从eclipse导出的,在gradle文件中添加如下代码:</p>
android {
sourceSets{
main{
jniLibs.srcDir['libs']
}
}
}
2.如果你在Android studio中创建项目:
在 src/main/ 中创建一个名为 jniLibs 的文件夹,并将你的 *.so 文件放在 jniLibs 文件夹中。
并在你的 gradle 文件中复制如下代码:</p>
android {
sourceSets{
main{
jniLibs.srcDir['jniLibs']
}
}
}