android JNI 技术初步探索 ----- 效率
JNI 全称 java native interface,即本地接口, 是一种在 java 中调用本地 C/C++ 方法的技术;
使用 JNI 编程有好处也有坏处,今天我们来探索一下 JNI 编程的效率问题;
使用 JNI 编程的实现过程
准备工作
配置 ndk,打开 project structure, 在如下位置有一个配置 Android NDK location
如果没有可以下载后再配置
1、编写一个 java 类,并在 java 类中申明 native 方法
public class TestJNI { static native int add(int a,int b); }
申明了一个 add 方法
2、打开 dos 命令窗口,进入到 TestJNI 类所在的文件夹,执行 javac 文件名.java 将 TestJNI 类变异成 .class 文件
3、使用 cd .. 命令退回到 src/main/java 目录, 执行 javah -jni 包名.类名,将 .class 文件编译成 .h 文件
执行完之后,会在 src/main/java 目录下生成一个 com_study_hugy_testjnidemo_NativeJNI.h 文件,文件内容如下
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_study_hugy_testjnidemo_NativeJNI */ #ifndef _Included_com_study_hugy_testjnidemo_NativeJNI #define _Included_com_study_hugy_testjnidemo_NativeJNI #ifdef __cplusplus extern "C" { #endif /* * Class: com_study_hugy_testjnidemo_NativeJNI * Method: jieCheng * Signature: (I)I */ JNIEXPORT jint JNICALL Java_com_study_hugy_testjnidemo_NativeJNI_add (JNIEnv *, jclass, jint, jint); #ifdef __cplusplus } #endif #endif
主要就是我们 编辑的 add 方法接口
4、在 scr/main/java 下新建一个 jniFolder "jni",并将第三步得到的文件复制进去,并且新建一个文件 NativeJNI.c, 当然名字随你的意思取, 这个类的主要目的就是实现 .h 文件中的方法
#include<com_study_hugy_testjnidemo_NativeJNI> JNIEXPORT jint JNICALL Java_com_study_hugy_testjnidemo_NativeJNI_jieCheng (JNIEnv *env, jclass jobj, jint a, jint b) { return a+b; }
5、在项目的 gradle.properties 文件中加入
android.useDeprecatedNdk=true
并且在 app 的 build.gradle 文件中加入
android { compileSdkVersion 26 defaultConfig { ... ndk { moduleName "NativeJni"//指定生成的so文件名 abiFilters "armeabi-v7a", "x86"//cpu的类型 } } ... sourceSets { main { jni.srcDirs = ['src/main/jni', 'src/main/jni/'] } } }
同步资源文件,然后 rebuild 工程,这时会发现一个错误
这个错误是说 android.useDeprecatedNdk 已经不再支持了,并且会在 android studio 的下一个版本中移除,震惊脸,WTF,不支持了,玩犊子啊。
不要急,接着往下看,看到最后一句话,To continue using ... ,set android.deprecatedNdkConpieLease=XXX ,哦, 删掉 android.useDeprecatedNdk=true, 以 android.deprecatedNdkConpieLease=XXX 替代, 同步资源文件 ,重新 rebuild ,完美通过,这时你会在build/intermediate/ndk 目录下发现生成的 so 文件
最后就是使用了
System.loadLibrary("NativeJNI");
System.out.println(NativeJNI.add(3,4));
到这里 JNI 技术的基本用法就基本掌握了,回到最初的问题,探索 JNI 技术的优点之效率;
如何探索呢,我用到的办法是对一个很大的数 for 循坏,查看循环前后的时间差
JNI 方法如下
#include<com_study_hugy_testjnidemo_NativeJNI.h> #include<time.h> JNIEXPORT jint JNICALL Java_com_study_hugy_testjnidemo_NativeJNI_jieCheng (JNIEnv *env, jclass jobj, jint num) { long time_1; time(&time_1); int i; int result = 1; for(i = 1; i <= num; i++) { result = result * i; } long time_2; time(&time_2); return time_2 - time_1; }
这个方法得到的时间是 s, 所以传入的数最好 大一些;
java 方法就比较简单了
public int jiecheng(int num) { long time_1 = SystemClock.currentThreadTimeMillis(); int result = 1; for(int i = 1; i<=num; i++) { result *= i; } long time_2 = SystemClock.currentThreadTimeMillis(); return time_2 - time_1; }
这个方法得到的时间单位是 ms
传入 1000000000,发现运行结果如下
(我的电脑)jni 在 1s 之内运算完毕, 而 java 需要 4s ,确实 JNI 的效率比值 java 高不少, 特别是在复杂的运算中。
其他优点待补充....