Android JNI开发摘录(五)之对象引用处理
六.本机代码中的对象引用
终于讲到对象引用了,在此推荐JNI开发必看的
使用 Java Native Interface 的最佳实践
JNI提供了函数集来使用本机代码中的Java对象,包括前面已经介绍了的串、数组和一般对象。那么,JNI到对象的引用是如何被处理的呢?更确切一些,垃圾收集程序是如何处理对象引用,并且知道什么时候收集垃圾对象呢?JNI提供了3种不同的引用类型:
本地引用:(Local Reference)只用在一个单一本机方法中。
全局引用:(Global Reference)在本机方法的多次调用之间使用。
弱全局引用:(Weak Global Reference)同全局引用一样,但是该类型引用无法组织对象实施垃圾收集。
1.本地引用:
本地引用是通过使用NewLocalRef函数来显式创建的,尽管许多JNI函数都会返回一个本地引用,这些引用都只是用于本机方法执行期间,在本机函数返回时消失。本地引用不应该在本机端被缓存(例如在一个本地静态变量中被保存),因为他们不能用在本机方法的多次调用中。只要本机函数返回,任意存在的本地引用都会被进行垃圾收集。如果希望函数返回之前回收本地引用,可以使用DeleteLocalRef函数来显式回收本地引用。本地引用只在创建他们的线程中有效,因此不要试图存储一个本地引用并在另一条线程中使用。
显式创建本地引用:
jobject NewLocalRef(jobject ref);
显式释放本地引用:
void DeleteLocalRef(jobject obj);
2.管理本地引用
有必要知道当前有多少本地引用被使用,因为许多函数都返回本地引用。JNI需要设置本地引用的最大值。同时,如果创建了大对象的引用,就有耗尽可用存储器的风险。下面的函数用于本地引用的管理:
/*该函数确保至少可以按照最小值创建本地引用。VM则确保当一个本机方法被调用时,至少有16个本地引用可以被创建。如果试图创建超过可用数量的更多本地引用,将会调用一个FatalError。该函数如果调用成功,则返回零;如果抛出一个OutOfMemoryException,则返回一个负值。*/
jint EnsureLocalCapacity(jint capacity);
/* PushLocalFrame是一个创建本地引用新作用域的有用函数,这使得PushLocalFrame函数可以释放其使用的框架中所有已分配的本地引用。当该函数被调用时,本地引用的最低数量将在本框架中被创建。该函数如果执行成功则返回0,如果由于错误抛出一个OutOfMemoryException,则返回一个负值。*/
jint PushLocalFrame(jint capacity);
/*PopLocalFrame函数释放当前框架中的所有本地引用(弹出一个框架)。因为存储该函数的结果(返回值)可能会导致在即将被弹出的框架中创建一个本地引用,该函数接收一个可以导致引用在当前框架被弹出之后的最高框架中创建的参数。这就确保可以维护一个存储PopLocalFrame函数结果的引用。*/
jobject PopLocalFrame(jobject result);
3.全局引用和弱全局引用
全局引用在一个本机方法的多次不同调用之间使用。他们只能通过使用NewGlobalRef函数来创建。全局引用可以在几个线程之间使用。全局引用提供了诸多好处。但是有一个小问题:Java无法控制全局引用的生命周期。用户必须自行判断全局引用何时不再需要,同时使用DeleteGlobalRef来释放他。弱全局引用与全局引用十分类似,但是其基本对象可以在任何时候进行垃圾收集。JNI提供一个IsSameObject的特定调用来找出基本对象是否仍然有效。
1)创建全局引用和弱全局引用:
/* NewGlobalRef创建一个新的全局引用,并且返回该引用。NewWeakGlobalRef创建并返回一个弱全局引用。它们的参数就是要创建对象的类。如果没有一个到类的句柄,可以通过FindClass来获得一个类。如果试图创建到空对象的引用,或者对象无法被创建,将返回NULL。如果由于没有可用存储器而使得引用无法被创建,则抛出一个OutOfMemoryException。*/
jobject NewGlobalRef(jobject lobj);
jweak NewWeakGlobalRef(jobject obj);
2)删除全局和弱全局引用
void DeleteGlobalRef(jobject gref);
void DeleteWeakGlobalRef(jweak ref);
4.引用比较
JNI提供一个特定函数,IsSameObject,用于测试在两个引用之后的对象是否相同。对于C++来说,关键字NULL对应于Java中的一个空对象。这样,就可以将NULL作为一个参数传入IsSameObject或者直接将一个对象引用同NULL比较。
/* 如果对象是相同的,返回JNI_TRUE,否则返回JNI_FALSE。如果试图使用IsSameObject来对一个弱全局引用同NULL作比较,如果基本对象没被垃圾收集,则返回JNI_TRUE,否则返回JNI_FALSE。*/
jboolean IsSameObject(jobject obj1,jobject obj2);