JNI类型签名介绍,JNI中垃圾回收、异常处理

来源:互联网 发布:linux查看打开文件数 编辑:程序博客网 时间:2024/05/16 08:10

摘录、参考文档:

1.深入理解Android:卷I,作者邓凡平;


上一章,讲了java与JNI之间的数据类型转换,JNIEnv介绍、操作jobject,jstring介绍;

本章,将讲关于JNI类型签名介绍,JNI中垃圾回收的一些知识。

1.JNI类型签名介绍

讲函数签名之前,先来看看JNI动态注册方法中的一段代码:

上面的代码我们已经见过了,但是其中那么长的字符串“(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V”是什么意思呢?

根据前面的讲解可知,它是java中对应的函数签名信息,由参数类型和返回值类型共同组成。不过为什么需要这个签名信息呢?

这个问题的答案比较简单。因为java支持函数重载,也就是说,可以定义同名但不同参数的函数。但仅仅根据函数名是没法找到具体函数的。为了解决这个问题,JNI技术中就将参数类型和返回值类型的组合作为了一个函数的签名信息,有了签名信息和函数名,就能顺利的找到java中的函数了。

JNI规范定义的函数签名信息看起来很别扭,不过习惯就好了。它的格式是:

(参数1类型表示参数2类型表示...参数n类型表示)返回值类型表示

来看processFile的例子:

函数签名不仅看起来麻烦,写起来更麻烦,稍微写错一个标点就会导致注册失败。所以,在具体编码时,读者可以定义字符串宏,这样改起来也方便。

上面列出了一些常用的类型标识。请注意,如果java类型是数组,则标识前面会有一个“[”,另外,引用类型(除基本类型的数组外)的标识最后都有一个“;”。

再来看几个小例子:

虽然函数签名信息很容易写错,但java提供一个叫javap的工具能帮助生成函数或变量的签名信息,它的用法如下:
javap -s -p xxx
其中,xxx为编译后的class文件,s表示输出内部数据类型的签名信息,p表示打印所有函数和成员的签名信息,默认只会打印public成员和函数的签名信息。

2.垃圾回收
我们知道,java中创建的对象最后是由垃圾回收器来回收和释放内存的,可它对JNI有什么影响呢?下面看一个例子:

上面的做法肯定有问题,因为和save_thiz对应的java层的MediaScanner很有可能已经被垃圾回收了,也就是说,save_thiz保存的这个jobject可能是一个野指针,如果使用它,后果会很严重。

可能有人要问,对一个引用类型执行赋值操作,它的引用计数不会增加吗?而垃圾回收机制只会保证那些没有被引用的对象才会被清理。问的对,但如果在JNI层使用下面这样的语句,是不会增加引用计数的。

save_thiz = thiz;  //这种赋值不会增加jobject的引用计数。

引用计数没有增加,thiz就有可能被回收,那该怎么处理这个问题?其实,JNI规范已经很好的解决了这个问题,JNI技术一共提供了三种类型的引用,它们分别是:

1)Local Reference:本地引用。在JNI层函数中使用的非全局引用对象都是Local Reference,它包括函数调用时传入的jobject和在JNI层创建的jobject。Local Reference最大的特点就是,一旦JNI层函数返回,这些jobject就可能被垃圾回收。

2)Global Reference:全局引用。这种对象如不主动释放,它永远不会被垃圾回收。

3)Weak Global Reference:弱全局引用。一种特殊的Global Reference,在运行过程中可能会被垃圾回收。所以在使用它之前,需要调用JNIEnv的IsSameObject判断它是否被回收了。

平时用得最多的是Local Reference和Global Reference,下面来看一个实例,代码如下:

每当JNI层想要保存java层某个对象时,就可以使用Global Reference,使用完后记得释放它就可以了。这一点很容易理解。下面要讲有关Local Reference的一个问题,还是先看实例,代码如下所示:

所以,没有及时回收Local Reference或许是进程占用内存过多的一个原因,请务必注意这一点。


3.JNI中的异常处理
JNI中也有异常,不过它和c++、java的异常不太一样。如果调用JNIEnv的某些函数出错了,则会产生一个异常,但这个异常不会中断本地函数的执行,知道从JNI层返回到java层后,虚拟机才会抛出这个异常。虽然在JNI层中产生的异常不会中断本地函数的运行,但一旦产生异常后,就只能做一些资源清理工作了(例如释放全局引用,或者ReleaseStringChars)。如果这时调用除上面所说函数之外的其它JNIEnv函数,则会导致程序死掉。

来看一个和异常处理有关的例子,代码如下:

JNI层函数可以在代码中截获和修改这些异常,JNIEnv提供了三个函数给予帮助:

1)ExceptionOccurred函数,用来判断是否发生异常;

2)ExceptionClear函数,用来清理当前JNI层中发生的异常;

3)ThrowNew函数,用来向java层抛出异常。

异常处理是JNI层代码必须关注的事情,读者在编写代码时务必小心对待。




0 0