蓝蓝的天 2019-06-20
The Android NDK is a toolset that lets you implement parts of your app using native-code languages such as C and C++. For certain types of apps, this can help you reuse code libraries written in those languages.
实现在你的Android应用中调用C程序实现一些功能。那为什么要在Java开发中使用C语言呢?
代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。
PS:资料来源
虽然关于NDK开发的学习资料不多,Google官方并很多关于NDK开发文档,但竟然NDK开发包那必然有它的意义。所以我们有必要对NDK开发有个初步认识。这节主要搭建NDK开发环境并在应用中调用native函数方法。
从ndk-7后,谷歌已经改良了ndk的操作,不需用使用cygwin来交叉编译,所以这里就不再讲cygwin的配置。所搭建的开发环境是Android Studio
1.在官方下载Android NDK开发包 下载地址目前最新的版本为r12b
2.添加java系统环境变量(如果Android Studio的terminal中无法使用javah指令的时候需要做此步操作)
变量 | 值 |
---|---|
CLASSPATH | .;%JAVA_HOME%lib; |
JAVA_HOME | X:XXJavajdk1.8.0_05 |
PATH | %JAVA_HOME%bin; |
3.新建Android工程,添加项目构建NDK本地路径
4.local.propertices中添加ndk.dir=c:\Android-ndk
5.gradle.properties文件中添加android.useDeprecatedNdk=true
1.新建类JniFunction,实现两个native方法
public class JniFunction { static public native String getJniString();//获取字符串 static public native int addNumber(int a,int b);//获取两个整数相加值 }
2.rebuild Project,在工程目录下app/intermediates/classes/debug下查找到JniFunction.class
3.在main目录下创建一个jni文件夹
4.打开Android Studio的terminal,使用cd定位在Android工程的app目录下输入
javah -d jni -classpath ..\..\build\intermediates\classes\debug com.xxxxxxx.xxx.JniFunction //切记debug和com之间有空格
5.在jni文件夹中就生成了com_learnncode_learnndk_JniFunction.h的头文件
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_learnncode_learnndk_JniFunction */ #ifndef _Included_com_learnncode_learnndk_JniFunction #define _Included_com_learnncode_learnndk_JniFunction #ifdef __cplusplus extern "C" { #endif /* * Class: com_learnncode_learnndk_JniFunction * Method: getJniString * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_learnncode_learnndk_JniFunction_getJniString (JNIEnv *, jclass); /* * Class: com_learnncode_learnndk_JniFunction * Method: addNumber * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_learnncode_learnndk_JniFunction_addNumber (JNIEnv *, jclass, jint, jint); #ifdef __cplusplus } #endif #endif
6.在jni文件夹下新建C/C++ Source File,复制h头文件中定义的函数实现函数内容。需要注意的是选择新建.c文件而不是.cpp,另外记得多添加一个无用的c文件,不然编译可能会出错。
#include "com_learnncode_learnndk_JniFunction.h" /* * Class: com_learnncode_learnndk_JniFunction * Method: getJniString * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_learnncode_learnndk_JniFunction_getJniString (JNIEnv * env, jclass obj){ return (*env)->NewStringUTF(env,"Hello Android NDK"); } /* * Class: com_learnncode_learnndk_JniFunction * Method: addNumber * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_learnncode_learnndk_JniFunction_addNumber (JNIEnv * env, jclass obj, jint a, jint b){ return a + b; }
7.在app build.gradle添加ndk配置
defaultConfig { applicationId "com.learnncode.learnndk" minSdkVersion 14 targetSdkVersion 24 versionCode 1 versionName "1.0" ndk{ moduleName "JniFunction" //设置库(so)文件名称 } }
8.在jni调用的地方添加以下内容
static { System.loadLibrary("JniFunction"); } //JniFunction就是build.gradle中NDK的文件名
1.so文件的生成
在app build.gradle中设置ndk生成so文件库文件名
ndk{ moduleName "JniFunction" //设置库(so)文件名称 abiFilters "armeabi","armeabi-v7a","x86" }
编译项目后在项目目录下ndk文件中就能看到生成的so文件
ABI:指应用基于哪种指令集来进行编译,ABI总共有四种,分别是armeabi、armeabi-v7a、mips、x86,它们都是表示cpu的类型。
安卓支持三类处理器(CPU):ARM、Intel和MIPS。ARM无疑被使用得最为广泛。Intel因为普及于台式机和服务器而被人们所熟知,然而对移动行业影响力相对较小。MIPS在32位和64位嵌入式领域中历史悠久,获得了不少的成功,可目前Android的采用率在三者中最低。
2.Android studio2.2环境下目录变化
更新了Android Studio之后发现加载目录发现了变化,原来的Jni文件放置在了单独的文件目录下
Project
Android
Android项目调用.so文件方法:在src/main/目录下新建jniLibs文件夹把ndk abiFilters生成的so文件拷贝在此目录下,再在build.gradle defultConfig中配置jniLibs路径,看过其他人写的默认写的是libs好像也是可运行的,这里我还是填上正式新建时的路径
sourceSets.main{ jniLibs.srcDirs "src/main/jniLibs" }
NDK开发环境的搭建算不上特别难,就是麻烦了些。之前看别人的教程说NDK r10b版本是比较稳定的,我使用的是r12b也能编译成功,目前看来还是可以使用的。这节先为NDK开发开一个头,其中也没详细讲解关于NDK开发的语法等内容。学习NDK的道路还是任重而道远啊。附上google官方的NDK开发Demo实例
本节Demo项目地址