Android 工作室,gradle 和 NDK

我对于这个级别和 Android Studio 的支持是非常新的。我已经设法转换我的 android 项目,使用导出选项梯度。

但是我正在寻找一些文档或起点如何集成 NDK 构建到梯度构建过程。

如果可能的话,我还需要一些“之后”阶段来复制构建二进制文件(。所以文件)到资产目录。

178988 次浏览

我们在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 的安卓工作室现在已经发布了: < a href = “ http://tools.Android.com/tech-docs/Android-NDK-view”> http://tools.Android.com/tech-docs/Android-NDK-preview

对于脚本构建,下面的渐变解决方案应该可行:

我正在使用我的构建脚本并添加到我的文件中(似乎适用于 0.8+) : 这似乎相当于下面的解决方案(但在 gradle 文件中看起来更好) :

 android {
sourceSets {
main {
jniLibs.srcDirs = ['native-libs']
jni.srcDirs = [] //disable automatic ndk-build
}
}
}

遗憾的是,如果目录不存在或不包含 .so文件,则构建不会失败。

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 的答案是最新的分级版本,但仍然有办法做到这一点。 在项目目录的根目录中创建一个 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'

正如 Xavier 所说,如果您使用的是 gradle 0.7.2 + ,那么您可以将预构建放在/src/main/jniLibs/中

取自: https://groups.google.com/d/msg/adt-dev/nQobKd2Gl_8/ctDp9viWaxoJ

如果你使用 unix,最新版本(0.8)会添加 ndk-build:

android.ndk {
moduleName "libraw"
}

它希望在‘ src/main/JNI’下找到 JNI,否则您可以使用:

sourceSets.main {
jni.srcDirs = 'path'
}

2014年1月28日,0.8版本的 build 在 windows 上被破坏,你必须使用以下方法禁用 build:

sourceSets.main {
jni.srcDirs = [] //disable automatic ndk-build call (currently broken for windows)
}

详细说明纳克索斯说过的话(感谢纳克索斯给我指明了正确的方向!),我从最近发布的 NDK 例子中学到了很多,并在这里发布了一个类似问题的答案。

如何配置 NDK 与 Android Gradle 插件0.7

本文详细介绍了如何将预构建的本地库链接到不同架构的应用程序中,以及如何将 NDK 支持直接添加到 build.gradle 脚本中。在大多数情况下,您不应该再需要围绕 zip 和复制进行工作。

我发现“ gradle 1.11 com.android.tools.build: gradle: 0.9. +”现在支持预构建 ndk,你可以把 * . so 放到 dir src/main/jniLibs 中。 当建筑梯级将包装的 ndk 到正确的地方。

这是我的项目

Project:
|--src
|--|--main
|--|--|--java
|--|--|--jniLibs
|--|--|--|--armeabi
|--|--|--|--|--.so files
|--libs
|--|--other.jar

在 Google IO 2015中,Google 宣布在 Android Studio 1.3中完全集成 NDK。

它现在是预览,并提供给每个人: https://developer.android.com/studio/projects/add-native-code.html

旧的答案: 如果您的项目源中有一个 jni目录,Gradle 会自动调用 ndk-build

这是在 Android 工作室0.5.9(金丝雀建立)工作。

  1. 下载 NDK

  2. 要么将 ANDROID_NDK_HOME添加到环境变量中,要么在 Android Studio 项目中将 ndk.dir=/path/to/ndk添加到 local.properties中。这允许 Android 工作室自动运行 ndk。

  3. 下载 最新的分级样本项目以查看 ndk 项目的示例。(它们在页面的底部)。一个好的样本项目是 ndkJniLib

  4. 从 NDK 样本项目中复制 gradle.build。就像这样。这个 gradle.build为每个体系结构创建一个不同的 apk。您必须使用 build variants窗格选择所需的体系结构。 build variants pane

    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 build 脚本中显式地调用 ndk-build,因为您刚刚禁用了自动调用。

task ndkBuild(type: Exec) {
commandLine 'ndk-build', '-C', file('src/main/jni').absolutePath
}


tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}

这是我使用 gradle 中的 android-ndk 构建的代码。为此,在 gradle.properties中添加 ndk 目录路径。添加 ndkdir=/home/user/android-ndk-r9d并将所有 jni 文件放在 src/main/中的 native文件夹中,从下面发布的代码中可以看到这一点。它将创建带有本机库的 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'

只需将这些行添加到应用程序 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)
}

到目前为止(Android Studio v0.8.6)它非常简单。下面是创建“ Hello world”类型应用程序的步骤:

  1. 下载 Android NDK 并将根文件夹放在合理的位置——可能与 SDK 文件夹放在同一位置。

  2. local.properties文件中添加以下内容: ndk.dir=<path-to-ndk>

  3. 将以下内容添加到 defaultConfig闭包中紧跟在 versionName行之后的 build.gradle 文件中: ndk { moduleName="hello-world" }

  4. 在应用程序模块的 main目录中,创建一个名为 jni的新文件夹。

  5. 在该文件夹中,创建一个名为 hello-world.c的文件,如下所示。

  6. 有关如何调用方法(或者它是一个函数?)的示例,请参见下面的示例 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'])
}

在这里找到一个非常类似的应用程序的完整源代码(不包括 NDK)。

配置项目在 机器人工作室中从 Eclipse: 你必须导入 eclipsendk 项目到 android 工作室,而不是导出到 gradle,它的工作,还需要在 本地物业中添加 ndk 的路径,如果显示错误,然后添加

sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] //disable automatic ndk-build callenter code here
}

建造,分级文件然后创建 Jni文件夹和文件使用终端和运行它将工作

下面是我使用 NDK 在我的 Android Studio 项目中工作的步骤。 我用这个教程来帮助我 Https://software.intel.com/en-us/videos/using-the-ndk-with-android-studio

为了使用 NDK,您必须向 local.properties 添加 NDK 行

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)

C 旗对我来说仍然是一种黑魔法。我还没有找到一个好的来源,所有的选择和他们给我的。在 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());

随着 Android Studio 升级到1.0,NDK 工具链支持得到了极大的改善(注意: 请阅读我在本文底部的更新,看看新的实验级别插件和 Android Studio 1.5的使用情况)。

Android Studio 和 NDK 集成得足够好,所以你只需要在你的模块 build.gradle 中创建一个 NDK {}块,并将你的源文件设置到(模块)/src/main/jni 目录中——然后你就完成了!

不再从命令行进行 ndk-build。

我已经在我的博客文章 http://www.sureshjoshi.com/mobile/android-ndk-in-android-studio-with-swig/中写了所有关于它的内容

重点是:

有两件事你需要知道。默认情况下,如果您有想要加载到 Android 应用程序中的外部库,默认情况下会在(模块)/src/main/jniLibs 中查找它们。可以通过在模块的 build.gradle 中设置 sourceSets.main.jniLibs.srcDir 来更改这一点。您需要一个子目录,其中包含您所针对的每个架构的库(例如 x86、 arm、 mips、 arm64-v8a 等等)。.)

您希望 NDK 工具链在默认情况下编译的代码将位于(module)/src/main/jni 中,与上面类似,您可以通过在模块的 build.gradle 中设置 sourceSets.main.jni.srcDirs 来更改它

把这个放到你的模块中:

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 repo: 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 repo (在开发部门) ,使用 Android Studio 1.5和最新的实验级插件(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 使用“原生”关键字自动完成了 C + +-Java 生成的包装器:

Example of auto-complete of C++-Java wrapper

然而,这并不完全是乐观的... ... 如果你使用 SWIG 来包装一个库来自动生成代码,然后尝试使用本地关键字自动生成,它会把代码放在你的 SWIG _ wrap.cxx 文件中错误的位置... ... 所以你需要把它移动到“ externC”块:

C++-Java wrapper moved to correct location

更新: 2017年10月15日

如果我没有提到 Android Studio 2.2以上版本通过 Gradle 和 CMake 对 NDK 工具链提供了基本的“原生”(不是双关语)支持,那就是我的失职了。现在,当您创建一个新项目时,只需选择 C + + 支持就可以了。

您仍然需要生成自己的 JNI 层代码,或者使用上面提到的 SWIG 技术,但是现在 Android 项目中 C + + 的脚手架已经很简单了。

在 CMakeList 文件中的更改(也就是放置 C + + 源文件的地方)将被 Android Studio 接收,并且它将自动重新编译任何相关的库。

我使用了下面的代码来编译本地 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)
}

现在,我可以装载这么成功!

将. so 文件添加到此路径

Project:

Src 总部 爪哇咖啡 | —— | —— | —— jniLibs 阿米阿比 文件

2. 将此代码添加到 gradle.build

android {
splits {
abi {
enable true
reset()
include 'x86', 'x86_64', 'arm64-v8a', 'armeabi-v7a', 'armeabi'
universalApk false
}
}

}

3. System.loadLibrary("yousoname");

  1. 祝你好运,1.2.3级就可以了

现在 Android Studio 处于稳定通道中,运行 Android-ndk 样本非常简单。这些示例使用 实验性插件,比 Android NDK 在线文档中链接的示例更新。一旦知道它们可以工作,就可以研究 build.gradle、 local.properties 和 gradle 包装器。属性文件,并相应地修改项目。下面是让它们工作的步骤。

  1. 进入设置,外观和行为,系统设置,Android SDK,选择 SDK 工具标签,并检查 Android NDK 版本1.0.0在列表的底部。这将下载 NDK。

  2. 指向新下载的 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、本机编解码器和 Builder。我使用以下方法:

Android Studio 1.3 build AI-141.2117773
Android-ndk 样本发布于2015年7月28日(以上链接)
SDK 工具24.3.3
提取 NDK r10e 到 C: Android sdk NDK-bundle
2.5级
Gradle 插件0.2.0
Windows 8.164位

我在 android 工作室项目的 本地物业文件中使用了 ndk < br > < br > 的 ndk.dir=/usr/shareData/android-ndk-r11b//path 在 android 工作室项目的 Gradle 房地产公司文件中添加以下行: < br > android.useDeprecatedNdk=true < br > < br > 。

更多信息请点击这里: http://tools.android.com/tech-docs/android-ndk-view”rel = “ nofollow”> http://tools.android.com/tech-docs/android-ndk-preview

  1. 如果您的项目从 eclipse 导出,请在 gradle 文件中添加以下代码:

    android {
    sourceSets{
    main{
    jniLibs.srcDir['libs']
    }
    }
    }
    

2.If you create a project in Android studio:

create a folder named jniLibs in src/main/ ,and put your *.so files in the jniLibs folder.

And copy code as below in your gradle file :

android {
sourceSets{
main{
jniLibs.srcDir['jniLibs']
}
}
}

虽然我相信 SJoshi (甲骨文的家伙)有最完整的答案,SWIG 项目是一个特殊的情况,有趣和有用的,在这一点上,但没有普遍适用于大多数项目,已经做了很好的标准 SDK 蚂蚁为基础的项目 + NDK。我们都希望现在使用 Android 工作室最有可能,或者想要一个更友好的 CI 构建工具链的移动,这在理论上提供了梯度。

我已经发布了我的方法,从某处借用(我在 SO 上找到了这个,但是发布了一个应用 build.gradle: https://gist.github.com/truedat101/c45ff2b69e91d5c8e9c7962d4b96e841的要点)。简而言之,我建议如下:

  • 不要将项目升级到最新的等级构建
  • 在项目根目录中使用 com.android.tools.build: gradle: 1.5.0
  • 在应用程序项目中使用 com.android.application
  • 确保 gradle.properties 具有: android.useDeprecatedNdk = true (如果它抱怨的话)
  • 使用上面的方法可以确保您创建 Android.mk 文件的时间和精力不会被浪费掉。您可以控制要构建的目标 arch (s)。这些指令对 Windows 用户很友好,理论上他们应该能够在 Windows 上构建而不存在特殊问题。

在我看来,Android 的 Gradle 是一团糟,就像我喜欢所借用的 maven 概念和项目目录的固执结构一样。这个 NDK 功能已经“即将到来”了近3年多。

NDK 构建和等级(基本)

通常,使用 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'])
}

向项目添加预生成库(高级)

静态库(。A)在您的 NDK 构建将自动包括,但预先构建的动态库(。所以)将需要放置在 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 中。