什么是JNI?
安卓中java代码和c/c++本地代码之间的调用.
为什么要使用JNI?
安全性 :安卓java代码容易被反编译,而c/c++不反编译后都是汇编代码,基本看不懂
复用性: 有一些功能本地代码已经实现了,就没必要再用java实现一遍.
局限性: 有些直接操作硬件的代码java没法实现,只能用c来实现
效率:大计算量时,c代码执行效率远高于java代码
什么时候需要用到JNI?
登录时密码的加密算法不能让外界通过反编译获得,那么就定义到c代码中
公司特有算法(比如专业图像处理,视频处理算法等)不想被外面知道,可以在c中编写.
图像视频处理,大容量数组排序等操作
操作智能设备时用c代码直接操作,然后在java中调用,获得数据并加以显示等
JNI的基本开发流程是什么?
这要分几种情况:
一个人搞定全部c和java代码
或者: 已经有现成编译好的.so动态库和jni方法声明
或者: 有c工程师提供好的c或c++源文件和头文件
拿第一种情况来说,基本流程是:
1.专门建一个jni的包-JNI类,定义好本地方法,确定好传递的参数
2.java中的调用:
3.添加ndk路径:(要预先下载好)
4.为工程添加本地支持:需要输入库的名字
并在调用本地方法的类中加载这个库:
5.如果要写c代码,则需要修正jni文件夹中自动生成的文件.(C++不用)
6.进入到硬盘上本工程src目录下,javah命令,传入本地方法声明的完整类名,生成头文件:
7.将生成的头文件剪切到工程的jni目录下(记得删除src目录下的头文件).将方法的声明拷贝到.c文件中:
8.从ndk的目录下导入jni.h头文件–处理器平台需对应
9.为本地代码添加全平台支持:
Jni目录下建Application.mk文件,添加一行代码:APP_ABI := all
10.编写c代码
11.编写完c代码后,即可运行app,eclipse会自动编译生成库文件,并加入到libs文件夹中
那么,如果是第二种,就直接将已有的.so类库放到libs目录下,在java代码中调用处加载库,然后根据提供的方法声明或JNI类来调用方法即可.
如果是第3中,就在给工程添加本地支持后,直接把c工程师提供的的.c文件和.h文件拷贝到jni目录.
在Android.mk中注册c文件:
在自动生成的c源文件中包含提供的头文件,并调用提供的c源代码中的方法:(同时不要忘了在调用时导库,添加全平台支持等操作)
c代码怎么调用java代码?
java代码调用c代码很简单,直接调用定义好的native方法即可,那么c代码怎么调用java代码呢?
一步步分析:
首先,java调用本地代码时,java中的native方法都会映射成c中的java_包名类名方法名的一个方法,
同时,最少持有两个参数,*env:能够调用全部jni方法库的二级指针,jobject obj—java中调用native方法的对象的指针.那么,通过obj这个对象指针,就可以调用该对象的相应方法
同时,方法还可以传入其他对象,也可以调用其所具有的方法.
方法的调用原理:
jni通过ID来识别域(字段)和方法.要想调用一个对象的方法,先要拿到其方法ID.
方法ID的获得:
先将拿到该方法所属对象的字节码:传入对象或者完整的类名:
jclass (*GetObjectClass)(JNIEnv*, jobject);
jclass (*FindClass)(JNIEnv*, const char*);
jclass clazz = (*env)->FindClass(env,"com/itheima/logcat/MainActivity");
然后通过字节码,方法名,方法签名拿到方法ID:
注:方法签名可采用javap命令生成在命令行,然后拷贝过来:
javap -s -p -classpath . 类名
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID methodid = (*env)->GetMethodID(env,clazz,"showDialog","(Ljava/lang/String;)V");
调用方法:
需要对象,方法id,方法参数
实例:
(*env)->CallVoidMethod(env,obj,methodid,(*env)->NewStringUTF(env,msg));
java与c之间的参数传递
和java本身方法调用类似:
基本数据类型是值传递,
对象是引用传递:以c指针形式从java传到本地方法中,指针指向jvm中的数据结构
对于数组来说,可以在一系列的 Get/ReleaseArrayElement 函数中指定是返回直接指向原始数组的指针还是将数组拷贝到一段缓冲区中,然后返回缓冲区的指针.
JNI本身的效率问题:
VM进行java/native调用时的消耗是 java/java 的2~3倍
多数 VM 中,native/java 调用的消耗可以达到 java/java 调用的 10 倍.
其他待续:
*env二级指针的理解
c持有java引用时的生命周期和释放