首先声明,以下内容,大部分摘抄自《深入理解Android》一书,只是结合自己的理解作了些重点的梳理。
读这章之前,如果能看过JNI相关的书,那么看这章可能会有感觉一点。以前看JNI的文档,有点像谭浩强的C,比较注重实用性,而这本书,则比较主用原理性,有点像《c programme language》,看完之后会对JNI的总体印象和原理明白得透彻一点,而不是光是盲目的实用。
这章主要讲了JNI技术中的几个重要方面,包括
1. JIN函数的注册方法
2.Java和JNI层数据类型的转换
3.JNIEnv和jstring的实用方法,以及JNI中的类型签名
4.垃圾回收在JNI层中的实用,以及异常处理方面的知识
概述
首先,是以MediaScanner为例子的典型JNI层次图:
JNI库的名字可以随便取,但是Android平台基本上都采用“lib模块名_jni.so”的命名方式。
对于Java程序员来说,只要做两件事情:
a. 加载对应的JNI库。
在调用native函数前,任何时候,任何地方加载都可以。通行的做法是在类的static语句中加载,调用System.loadLibrary方法
b.声明由关键字native修饰的函数
JNI函数的注册方法
1.静态注册
2.动态注册
typedef struct{
const char* name; //Java中native函数的名字,不用携带包的路径,例如“native_init”
const char* signature; //Java函数的签名信息,用字符串表示,是参数类型和返回类型的组合,用以应对Java里的函数重载
void* fnPtr; //JNI层对应函数的函数指针,注意他是void*类型
} JNINativeMethod
jclass clazz = (*env)->FindClass(env,className); //className为对应的Java类名,在这里为“android/media/MediaScanner”
(*env)->RegisterNatives(env, clazz, gMethods, numMethods); //最终调用JNIEnv的RegisterNatives函数,注册关联关系。
Java和JNI层数据类型的转换
垃圾回收
save_thiz = thiz
因此,如果thiz引用计数为0,会被回收。为解决这一问题,JNI技术一共提供了三种类型的引用:
1. Local Reference:本地引用。一旦JNI层函数返回,这些jobject就可能被垃圾回收
2. Global Reference: 全局引用。如果不住动释放,它就永远不会被垃圾回收。
3. Weak Global Reference: 弱全局引用,在运行时有可能会被回收,在使用之前,需要调用JNIEnv的IsSameObject判断它是否被回收了
所以,每当JNI层想要保存Java层中的某个对象时,就可以使用Global Reference, 使用完之后记住释放它。
另外,虽然Local Reference在引用的函数退出之后会自动被回收,但是在不再需要用到时候,还是建议显式调用DeleteLocalRef来主动释放,因为没有即使回收Local Reference或许是进程占用内存过多的一个原因。
看完这一章,有以下不明白的地方:
1. 应该找时间看一下RegisterNatives这个函数到底做了些什么。
2.如果用静态注册的方法,一个JNI函数只能被注册到一个JAVA函数里,因为包名是严格遵守的。那么如果用动态注册的方法,能不能使一个JNI函数,同时被注册到两个不同包或者不同类的JAVA函数里呢?有空要试试。
3.关于JNIEnv和JavaVM的区别和相关的进程线程的东西,书上没有具体的例子,我也不大能搞明白