读完本章,你可以:
- 在Android Studio上轻松搭建NDK开发环境。
- 掌握最快捷的JNI开发方式。
- 掌握使用.so文件开发Android程序的技巧。
一、 NDK环境搭建
-
开发工具准备
要进行Android NDK开发,首先要下载Android NDK开发工具。可以在AndroidStudio上面下载,也可以自己下载好了,然后将NDK的路径设置为自己下载的Android NDK开发工具的路径。
- Gradle的相关配置
- 我们来配置Project的build.gradle。
/*gradle插件不支持NDK,我们需要使用gradle-experimental插件。 */
dependencies {
classpath 'com.android.tools.build:gradle-experimental:0.7.0'
}
- 我们来配置Module的build.gradle。
apply plugin: 'com.android.model.application'
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.2"
defaultConfig {
applicationId "com.lavor.ndklearning"
minSdkVersion.apiLevel 15
targetSdkVersion.apiLevel 23
versionCode 4
versionName "1.0.1"
}
}
android.ndk {
moduleName "lavor"
ldLibs.addAll(['log'])
cppFlags.add("-std=c++11")
cppFlags.add("-fexceptions")
platformVersion 15
stl 'gnustl_shared'
}
android.buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file("proguard-rules.txt"))
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:recyclerview-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
}
小提示
- 首先,在apply的时候我们引入的插件名称由'com.android.application'变成了'com.android.model.application'。
- 其次,在原来android的外层加入了一个model层次。
- 再次,原来在android的里面的块,除了defaultConfig外,全部移除android块放入model块中与android并列,并且前面的名字加上android.。
- 然后,compileSdkVersion 23与 buildToolsVersion "23.0.2"改成 compileSdkVersion = 23和 buildToolsVersion = "23.0.2",中间加上了=。
- 其次,添加上android.ndk块,块中的moduleName表示C/C++代码打包成so文件的名字。
- 再次,android.buildTypes块中的proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'改成proguardFiles.add(file("proguard-rules.txt"))。
- 最后,注意dependencies块依然在最外层,它不在model块中。
二、 使用NDK开发第一个JNI程序
- 在Android程序中新建一个包含native(本地)方法的NDKTest类。
package com.lavor.ndklearning;
public class NDKTest {
static {
//加载.so库文件
System.loadLibrary("lavor");
}
public native String getString();
}
小知识
- 程序中static{}称为静态代码块,它在类初始化的时候执行。不懂请猛击>>
-
将鼠标移动到方法名getString上,然后按下Alt+Enter快捷键,弹出一些解决的方法建议,点击第一个Create Function...
-
此时会自动建立一个与java目录同级的jni目录,在jni目录自动建立一个c文件,在c文件中实现了刚才的native方法。
- 然后,稍稍修改一下.c文件中实现的native方法。
JNIEXPORT jstring JNICALL
Java_com_lavor_ndklearning_NDKTest_getString(JNIEnv *env, jobject instance) {
// TODO
return (*env)->NewStringUTF(env, "AndroidStudio NDK开发最佳入门实践");
}
补充说明
- 仔细观察可以看到c文件的方法名是遵循“Java_包名类名本地方法名”来组织的(了解到这些后,我们就可以脱离编译器的帮助,直接去写.c文件啦)。
- 最后在Android程序中调用JNI接口。
public class MainActivity extends Activity {
private android.widget.TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.text = (TextView) findViewById(R.id.text);
NDKTest NDK=new NDKTest();
text.setText(NDK.getString());
}
}
运行程序后界面如下:
深度总结
- 讲道理,我们在正式开发时,不会像这样子去开发。因为这样的话就会把我们的c代码也暴露了出来。这明显与JNI的安全保密机制相违背,所以,正式开发的时候,我们会使用.so文件进行全保密式的开发。
三、 使用.so文件开发Android程序
不知道大家有没有记得上一章说过,使用JNI有个好处就是安全性高,JNI部分的代码很难被反编译。这其实是要归功于.so文件。下图为.so文件的生成位置。
具体的使用步骤是怎样的呢?
-
新建一个Android工程,命名为:AndroidApplication。(工程名、包名、组织名任意)
-
导入.so文件至app/libs中。
- 为了能够调用libs文件夹下的.so资源,需在app的build.gradle的android节点下设置。
sourceSets{
main{
jniLibs.srcDirs = ['libs']
}
}
- 新建相关类结构。
package com.lavor.ndklearning;
public class NDKTest {
static {
//加载.so库文件
System.loadLibrary("lavor");
}
public native String getString();
}
补充说明
- Android端声明native code的代码需要严格按照JNI接口来组织。(也就是说,包名、类名都要和.so文件中JNI名称一致)不懂就点击这里吧>>
- 函数调用。
public class DemoActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_demo);
Log.d("NDK",new NDKTest().getString());
}
}
运行结果如下:
注意啦!此文并非原创,算是对一波优秀文章的总结吧。笔者希望通过这种方式激励自己学习,也期待可以帮到正在学习NDK的你。欢迎指出本文的BUG,共勉!!!!!
参考资料