Android Studio1.5中NDK开发

Android中NDK的配置和使用。

从Android Studio1.3以后,在Android 环境开发JNI程序搭建开发环境变得相对简单。这里就来介绍一下基于Android Studio如何进行jni开发。在介绍之前,先跟大家说一下JNI和NDK都是什么,方便大家理解。

NDK和JNI的关系

JNI是java语言提供的Java和C/C++相互沟通的机制,Java可以通过JNI调用本地的C/C++代码,本地的C/C++的代码也可以 调用java代码。JNI 是本地编程接口,Java和C/C++互相通过的接口。Java通过C/C++使用本地的代码的一个关键性原因在于C/C++代码的高效性。
NDK是一系列工具的集合。它提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这 些工具对开发者的帮助是巨大的。它集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪 些文件需要编译”、“编译特性要求”等),就可以创建出so。它可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。

然后跟大家说一下环境的搭建

环境说明:

  • IDE:Android Studio 1.5
  • JDK: 1.7.0_67
  • Gradle:2.8
  • SDK with Build Tools at least version 23.0.2

使用Android Studio内置的SDK管理器下载NDK。

这里写图片描述

对NDK进行关联

这里写图片描述
这里写图片描述

这里也可以去修改工程下的local.properties

1
2
3
4
5
6
7
8
9
10
11
12
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Thu Mar 03 11:29:37 CST 2016
ndk.dir=C\:\\software\\sdk\\ndk-bundle
sdk.dir=C\:\\software\\sdk

设置gradle

设置好NDK之后,开始设置gradle,设置gradle主要需要设置三个地方,设置好之后就可以直接编写和编译JNI代码了,不需要像以前一样编写Makefile,相当方便。但是设置gradle也是需要比较小心的,由于当前NDK还处于Experimental 阶段,更新不断,经常会爆出各种奇怪的错误,因此也要特别留心。好了废话不多说,下面来介绍设置gradle的三个主要步骤。

首先设置TopLevel gradle,也就是Project gradle,这里比较简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
// classpath 'com.android.tools.build:gradle:1.5.0'
classpath 'com.android.tools.build:gradle-experimental:0.4.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

还要多提一句的是,这里设置的是gradle-experimental:0.4.0,后面对应设置gradle wrapper的时候要对应gradle2.8-all 版本,这里先说到这里。

1
2
3
4
5
6
#Wed Oct 21 11:34:03 PDT 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip

接着设置 Module gradle,这一步是比较麻烦的。由于我们在创建工程的时候自动生成的这个gradle文件内容比较多,而且如果要使用NDK的话这个gradle变化比较大,这里直接贴出需要使用NDK的gradle,然后来进行说明。

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
44
45
46
47
48
49
50
51
52
53
54
55
56
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.2"
defaultConfig.with {
applicationId = "com.oracleen.rl.easyar"
minSdkVersion.apiLevel = 15
targetSdkVersion.apiLevel = 23
versionCode = 1
versionName = "1.0"
}
}
android.ndk {
moduleName = "HelloARVideoNative"
cppFlags.add("-I${file("../../package/include")}".toString())
cppFlags.add("-DANDROID")
cppFlags.add("-fexceptions")
cppFlags.add("-frtti")
stl = "gnustl_static"
ldLibs.add("log")
ldLibs.add("GLESv2")
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file("proguard-rules.pro"))
}
}
android.productFlavors {
create("arm") {
ndk.with {
abiFilters.add("armeabi-v7a")
}
}
}
android.sources {
main {
jni {
dependencies {
library file("../../package/Android/libs/armeabi-v7a/libEasyAR.so") abi "armeabi-v7a"
}
}
}
}
}
dependencies {
// compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.0.1'
compile fileTree(dir: '../../package/Android/libs', include: ['*.jar'])
}

和自动生成的gradle相比,首先是 apply plugin: ‘com.android.application’ 变成了 apply plugin: ‘com.android.model.application’。下面的配置也需要包装在model{}中。

这个gradle的配置有几点需要注意的:

  1. 所有值的设置都要写成 xxx = yyyy的形式,比如: applicationId = “com.oracleen.rl.easyar” ,否则会报这种错误:Error:Cause: org.gradle.api.internal.ExtensibleDynamicObject, 当出现此类错误,检查是否都用了 “=”的方式。
  2. buildTypes 需要从android{} 中取出来,写成android.buildTypes{}的形式,否则会出现这种错误:Error:Unable to load class ‘org.gradle.nativeplatform.internal.DefaultBuildType_Decorated’. 内容也需要改为上边文件中的格式,否则会报这种错误:Error:No signature of method: org.gradle.model.ModelMap.minifyEnabled() is applicable for argument types: (java.lang.Boolean) values: [false]
  3. defaultConfig{} 需要写成defaultConfig.with{} 的形式,否则会报这种错误:Error:Cause: com.android.build.gradle.managed.AndroidConfig_Impl
  4. 在defaultConfig.with{} 中 需要写成上边格式,也就是比自动生成的多 .apiLevel ,否则会报这种错误:Unable to load class com.android.build.gradle.managed.ProductFlavor_Impl 
  5. 最后一点,在gradleWrapper中使用的是2.5,则android.ndk {} 中类似cppFlags 的添加使用 += 的方式,否则需要使用 .add的方式
  6. 引用的其他model构建方式也要包装在model{}当中。

NDK开发主要有两中情况:一种是使用已经编译好的.so动态库;一种是使用c/c++源代码开发。我会分别介绍。

编译c/c++源代码开发

这里我们起一个名字是“HelloARVideoNative”的JNI库,androidstudio会把我们编写的c/c++代码编译成这个名字的.so文件。

1
2
3
4
5
6
android.ndk {
moduleName = "HelloARVideoNative"
stl = "gnustl_static"
ldLibs.add("log")
ldLibs.add("GLESv2")
}

然后我们在我们需要使用c/c++方法的类中引用库,并且声明本地接口。

这里写图片描述

然后就可以去c/c++文件中去实现我们的方法了。

这里写图片描述

使用已经编译好的.so动态库

在app下的gradle中做如下配置

1
2
3
4
5
6
7
8
9
android.sources {
main {
jni {
dependencies {
library file("../../package/Android/libs/armeabi-v7a/libEasyAR.so") abi "armeabi-v7a"
}
}
}
}

指向我们的so库文件位置,然后在需要引用的地方

1
2
3
static {
System.loadLibrary("EasyAR")
}

然后我们就可以使用so库中的方法了。

这里写图片描述

最后,附上相关的资源.其实在android的官网已经提供了很完善的教程了.还有github上也托管了sample代码.

Experimental Plugin User Guide: http://tools.android.com/tech-docs/new-build-system/gradle-experimental
github sample: https://github.com/googlesamples/android-ndk
官方ndk视频教程: https://youtu.be/okLKfxfbz40 (需要翻墙)

任磊_Coder wechat
关注博主是一种态度,评论博主是一种欣赏。
坚持原创技术分享,您的支持将鼓励我继续创作!