ANDROID STUDIO, GRADLE AND NDK INTEGRATION
Originally posted on:http://ph0b.com/android-studio-gradle-and-ndk-integration/
With the recent changes (release 0.7.3 around Dec 27), the new Android Build System starts to be really interesting also if you are using the NDK!
Now this is really easy to integrate native libraries in your package and generate APKs for different architectures while correctly handling version codes (for more information on why this may be important, please refer to my first article).
update 2014/12/09: this article remains up-to-date if you’re using the recent android studio and gradle plugin’s 1.0 releases.
update 2014/11/1: changed output.abiFilter to output.getFilter(com.android.build.OutputFile.ABI) in order to work with gradle 0.14
update 2014/09/19: I’ve added information and modified my samplebuild.gradle to demonstrate the brand new APK Splits feature introduced by the latest android gradle plugin (0.13)
update 2: I’ve modified the sample build.gradle file to make it callndk-build scripts by itself.
update 1: Here is a screencast on how to set up a project with NDK sources from Android Studio:
Integrating .so files into your APK
If you are using Android Studio and need to integrate native libraries in your app, you may have had to use some complex methods before, involving maven and .aar/.jar packages… the good news is you don’t need these anymore
You only need to put your .so libraries inside the jniLibs folder under sub-directories named against each supported ABI (x86, mips, armeabi-v7a, armeabi), and that’s it !
Once it’s done, all the .so files will be integrated into your apk when you build it:
If the jniLibs folder name doesn’t suit you (you may generate your .so files somewhere else), you can set a specific location inbuild.gradle:
Java
1
2
3
4
5
6
|
android {
...
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
}
}
|
Building one APK per architecture, and doing it well !
You can use flavors to build one APK per architecture really easily, by using abiFilter property.
ndk.abiFilter(s) is by default set to all. This property has an impact on the integration of .so files as well as the calls to ndk-build (I’ll talk about it at the end of this article).
Let’s add some architecture flavors in build.gradle:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
android{
...
productFlavors {
x86 {
ndk {
abiFilter "x86"
}
}
mips {
ndk {
abiFilter "mips"
}
}
armv7 {
ndk {
abiFilter "armeabi-v7a"
}
}
arm {
ndk {
abiFilter "armeabi"
}
}
fat
}
}
|
And then, sync your project with gradle files:
You should now be able to enjoy these new flavors by selecting the build variants you want:
Each of these variants will give you an APK for the designated architecture:
app-x86-release-unsigned.apk
The fat(Release|Debug) one will still contain all the libs, like the standard package from the beginning of this blog post.
But don’t stop reading here! These arch-dependent APKs are useful when developing, but if you want to upload several of these to the Google Play Store, you have to set a different versionCode for each. And thanks to the latest android build system, this is really easy:
Automatically setting different version codes for ABI dependent APKs
The property android.defaultConfig.versionCode holds theversionCode for your app. By default it’s set to -1 and if you don’t change it, the versionCode set in your AndroidManifest.xml file will be used instead.
Hence if you want to be able to dynamically modify yourversionCode, you need to first specify it inside your build.gradle:
1
2
3
4
5
6
7
|
android {
...
defaultConfig{
versionName "1.1.0"
versionCode 110
}
}
|
But this is still possible to keep setting this variable only inside yourAndroidManifest.xml if you retrieve it “manually” before modifying it:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import java.util.regex.Pattern
android {
...
defaultConfig{
versionCode getVersionCodeFromManifest()
}
...
}
def getVersionCodeFromManifest() {
def manifestFile = file(android.sourceSets.main.manifest.srcFile)
def pattern = Pattern.compile("versionCode=\"(\\d+)\"")
def matcher = pattern.matcher(manifestFile.getText())
matcher.find()
return Integer.parseInt(matcher.group(1))
}
|
Once it’s done, you can prefix the versionCode inside your different flavors:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
android {
...
productFlavors {
x86 {
versionCode Integer.parseInt("6" + defaultConfig.versionCode)
ndk {
abiFilter "x86"
}
}
mips {
versionCode Integer.parseInt("4" + defaultConfig.versionCode)
ndk {
abiFilter "mips"
}
}
armv7 {
versionCode Integer.parseInt("2" + defaultConfig.versionCode)
ndk {
abiFilter "armeabi-v7a"
}
}
arm {
versionCode Integer.parseInt("1" + defaultConfig.versionCode)
ndk {
abiFilter "armeabi"
}
}
fat
}
}
|
Here I’ve prefixed it with 6 for x86, 4 for mips, 2 for ARMv7 and 1 for ARMv5. If you’re asking yourself why!? please refer to this paragraph I wrote before on architecture dependent APKs on the Play Store.
Improving multiple APKs creation and versionCode handling with APK Splits
Since version 0.13 of the android plugin, instead of having a product flavors to get multiple APKs, you can use splits to have a single build (and variant) that will produce multiple APKs (and it’s much cleaner and faster).
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
splits {
abi {
enable true // enable ABI split feature to create one APK per ABI
universalApk true //generate an additional APK that targets all the ABIs
}
}
// map for the version code
project.ext.versionCodes = ['armeabi':1, 'armeabi-v7a':2, 'arm64-v8a':3, 'mips':5, 'mips64':6, 'x86':8, 'x86_64':9]
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
output.versionCodeOverride =
project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + android.defaultConfig.versionCode
}
}
|
Compiling your C/C++ source code from Android Studio
If you have a jni/ folder in your project sources, the build system will try to call ndk-build automatically.
As of 0.7.3, this integration is only working on Unix-compatible systems, cf bug 63896. On Windows you’ll want to disable it so you can call ndk-build.cmd yourself. You can do so by setting this inbuild.gradle: this has been fixed
The current implementation is ignoring your Android.mk makefiles and create a new one on the fly. While it’s really convenient for simple projects (you don’t need *.mk files anymore !), it may be more annoying for projects where you need all the features offered by Makefiles. You can then disable this properly in build.gradle:
Java
1
2
3
4
|
android{
...
sourceSets.main.jni.srcDirs = [] //disable automatic ndk-build call
}
|
If you want to use the on-the-fly generated Makefile, you can configure it first by setting the ndk.moduleName property, like so:
Java
1
2
3
4
5
6
7
8
|
android {
...
defaultConfig {
ndk {
moduleName "hello-jni"
}
}
}
|
And you’re still able to set these other ndk properties:
- cFlags
- ldLibs
- stl (ie: gnustl_shared, stlport_static…)
- abiFilters (ie: “x86″, “armeabi-v7a”)
You can also set android.buildTypes.debug.jniDebugBuild to true so it will pass NDK_DEBUG=1 to ndk-build when generating a debug APK.
If you are using RenderScript from the NDK, you’ll need also to set the specific property defaultConfig.renderscriptNdkMode to true.
If you rely on auto-generate Makefiles, you can’t easily set differentcFlags depending on the target architecture when you’re building multi-arch APKs. So if you want to entirely rely on gradle I recommend you to generate different libs per architecture by using flavors like I’ve described earlier in this post:
Java
1
2
3
4
5
6
7
8
9
10
|
...
productFlavors {
x86 {
versionCode Integer.parseInt("6" + defaultConfig.versionCode)
ndk {
cFlags cFlags + " -mtune=atom -mssse3 -mfpmath=sse"
abiFilter "x86"
}
}
...
|
My sample .gradle file
Putting this altogether, Here is one build.gradle file I’m curently using. It’s using APK Splits to generate multiple APKs, it doesn’t use ndk-build integration to still rely on Android.mk and Application.mk files, and doesn’t require changing the usual location of sources and libs (sources in jni/, libs in libs/). It’s also automatically calling ndk-build script from the right directory:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.application'
android {
compileSdkVersion 21
buildToolsVersion "21.1"
defaultConfig{
minSdkVersion 16
targetSdkVersion 21
versionCode 101
versionName "1.0.1"
}
sourceSets.main {
jniLibs.srcDir 'src/main/libs'
jni.srcDirs = [] //disable automatic ndk-build call
}
project.ext.versionCodes = ['armeabi':1, 'armeabi-v7a':2, 'arm64-v8a':3, 'mips':5, 'mips64':6, 'x86':8, 'x86_64':9] //versionCode digit for each supported ABI, with 64bit>32bit and x86>armeabi-*
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
output.versionCodeOverride =
project.ext.versionCodes.get(output.getFilter(com.android.build.OutputFile.ABI), 0) * 1000000 + defaultConfig.versionCode
}
}
// call regular ndk-build(.cmd) script from app directory
task ndkBuild(type: Exec) {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine 'ndk-build.cmd', '-C', file('src/main').absolutePath
} else {
commandLine 'ndk-build', '-C', file('src/main').absolutePath
}
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
}
|
Troubleshooting
NDK not configured
If you get this kind of error:
1
2
|
Execution failed for task ':app:compileX86ReleaseNdk'.
> NDK not configured
|
This means the tools haven’t found the NDK directory. You have two ways to fix it: set the ANDROID_NDK_HOME variable environment to your NDK directory and delete local.properties, or set it manually inside local.properties:
1
|
ndk.dir=C\:\\Android\\ndk
|
No rule to make target
If you get this kind of error:
1
|
make.exe: *** No rule to make target ...\src\main\jni
|
This may come from a current NDK bug on Windows, when there is only one source file to compile. You only need to add one empty source to make it work again.
Other issues
You may also find some help on the official adt-dev google group:https://groups.google.com/forum/#!forum/adt-dev
Getting more information on NDK integration
The best place to get more information is the official project page: http://tools.android.com/tech-docs/new-build-system.
You can look at the changelog and if you scroll all the way down you’ll also get access to sample projects dealing with NDK integration, inside the latest “gradle-samples-XXX.zip” archive.
ANDROID STUDIO, GRADLE AND NDK INTEGRATION的更多相关文章
- 在Android studio中进行NDK开发
在Android studio中进行NDK开发 分类: Android平台 软硬件环境 ubuntu kylin 14.04 红米note增强版 Android studio 0.8.6 ndk ...
- [转]加速Android Studio/Gradle构建
加速Android Studio/Gradle构建 android android studio gradle 已经使用Android Studio进行开发超过一年,随着项目的增大,依赖库的增多, ...
- 解决Android Studio Gradle Build Running慢的问题
Android Studio方便好用,但是Android Studio Gradle Build Running很慢 解决方法: C:\Users\你的用户名\.gradle 目录下新建一个文件名为 ...
- Android studio gradle 打包 那些事
总结了一下 目前觉得比较好用的gradle 和一些打包 经验.放在这里. 首先说下 渠道号 这个概念,我们经常会统计我们的api 访问来源 是来自于那个app store,这有利于 我们针对性的推广. ...
- 解决Android Studio Gradle Build特别慢的问题
解决Android Studio Gradle Build 特别慢的问题 C:\Users\你的用户名\.gradle目录下新建一个文件名为gradle.properties的文件.内容为:org.g ...
- android studio gradle 两种更新方法更新
android studio gradle 两种更新方法更新 第一种.Android studio更新 第一步:在你所在项目文件夹下:你项目根目录gradlewrappergradle-wrapper ...
- 如何在Android Studio中指定NDK位置?
如何在Android Studio中指定NDK位置? 问题描述 NDK已经手工下载解包在本地: D:\Portable\android-ndk-r13b 每次创建支持C++项目时,都提示NDK没配置, ...
- android studio gradle 更新方法。
Android studio更新 第一步:在你所在项目文件夹下:你项目根目录gradlewrapper gradle-wrapper.properties (只要在打开项目的时候选OK,这个文件就 ...
- Android studio gradle配置完整版(转)
Android studio gradle配置完整版https://my.oschina.net/u/1471093/blog/539075 Android studio 自定义打包apk名 - pe ...
随机推荐
- Hadoop组成
Hadoop由以下几个子项目组成: Hadoop Common Hadoop体系最底层的一个模块,为Hadoop各子项目提供各种工具,如:配置文件和日志操作等. Avro Avro是doug cutt ...
- 史上最佳 Mac+PhpStorm+XAMPP+Xdebug 集成开发和断点调试环境的配置
在上一篇 PHP 系列的文章<PHP 集成开发环境比较>中,我根据自己的亲身体验,非常简略的介绍和对比了几款常用的集成开发环境,就我个人而言,比较推崇 Zend Studio 和 PhpS ...
- 防止被dylib hook的小技巧
在Build Settings中找到“Other Linker Flags”在其中加上”-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null”即可.
- 如何将后台传来的json反序列化为前端具体对象
//jQuery方式 var obj = $.parseJSON(json); .... //eval var obj = eval("("+json+")" ...
- Careercup - Google面试题 - 5680330589601792
2014-05-08 23:18 题目链接 原题: If you have data coming in rapid succession what is the best way of dealin ...
- 【转载】spring的普通类中如何取session和request对像
原文地址:http://blog.csdn.net/yousite1/article/details/7108585 首先要在web.xml增加如下代码: <listener> <l ...
- bzoj 2697 贪心
就贪心就行了,首先可以看成n个格子,放物品,那么 一个物品假设放3个,放在1,k,n处,那么价值和放在1,n 是一样的,所以一个物品只放两个就行了,价值大的应该尽量放 在两边,那么排序之后模拟就行了 ...
- bzoj 1565 最大权闭合子图
因为每个植物都有保护的点(每排相邻的右面的算是保护左面的),所以连他和保护 的点一条边,然后每个点有自己的权值,要找到最大的权值且满足每个点在访问时他 的前驱一定被访问,那么反向建边,转化为后继必须访 ...
- 【BZOJ】【3156】防御准备
DP/斜率优化 斜率优化的裸题…… sigh……又把$10^6$当成10W了……RE了N发 这题还是很水的 当然逆序也能做……不过还是整个反过来比较顺手 反转后的a[0]=反转前的a[n],以此类推直 ...
- Leetcode#68 Text Justification
原题地址 没有复杂的算法,纯粹的模拟题 先试探,计算出一行能放几个单词 然后计算出单词之间有几个空格,注意,如果空格总长度无法整除空格数,前面的空格长度通通+1 最后放单词.放空格,组成一行,加入结果 ...