Android JNI错误--原因和解决办法

这两天写程序,碰到JNI错误,具体的错误如下:
JNI ERROR(app bug):accessed stale local reference 0x1d300009 (index 2 in a table of size 0)
VM aborting
Fatal singal 6 (SIGABRT) at 0x000028b3 (code=-6),thread 10501 (Thread -7129)

折腾了一天,在网上找了一些资料,弄明白原因,现在跟大家一起分享一下。
早前的Android版本,虽然用的是直接指针,但是GC(garbage collector 垃圾回收器,下面简称GC)不会随便回收对象,其实这样的做法是不安全的。但是从Android 4.0+开始,修改了GC。GC现在的工作模式是:全局对象使用过程中可能会被回收。不同于之前的使用直接指针,现在是通过一个 handle (指针的引用)去操作。当 GC 回收对象时,只需要修改这张 handle 表即可,而表的维护工作显然交给底层就好了。

出现这个问题怎么解决呢?有连个方法去解决:
1.android 还是提供了JNI bug的兼容模式:在AndroidManifest.xml中将targetSdkVersion的版本改在14以下(也就是Android 4.0以下),这样就可以避免这个问题了。
2.修改 JNI 层代码原来直接通过 FindClass() 函数得到 static 对象的地方:
jclassRef = jniEnv->FindClass("android/graphics/BitmapFactory$Options");
改为通过调用 NewGlobalRef() 函数得到全局变量的引用,然后强制转换下类型:
jclass tmpClass = jniEnv->FindClass("android/graphics/BitmapFactory$Options"); 
jclassRef = (jclass)jniEnv->NewGlobalRef(tmpClass);

这个方法改正过后,targetSdkVersion的版本就可以改成14以上了。

下面再来说下关于JNI引用的一些知识:

在JNI中,有一些不同的引用。其中最重要的两种就是局部引用(local references)和全局引用(global references)。任意一个给定的object都可以是局部或是全局的。全局或者局部的区别同时影响生命周期和作用域。全局的可以在任意线程通过本线程的JNIEnv*使用,并且可以有效到明确调用DeleteGlobalRef()之时。局部的只能在其最初被递交到的线程中使用,并且可以有效到明确调用DeleteLocalRef()之时,或者,更普遍的,到你从你的原生函数中返回为止。当原生函数返回时,所有的局部引用都会被自动删除掉。

在之前的系统中,局部引用是直接的指针,局部引用永远不会真正变为不可用的。那就意味着你可以无限使用一个局部引用,即使你已经明确对它调用过DeleteLocalRef()了,或者使用PopLocalFrame()明确删除了它。

虽然任意JNIEnv*只能在一个线程中可用,但由于Android在JNIEnv*中从来没有保存过每个线程的状态,所以之前在错误的线程中使用JNIEnv*也不会出问题。现在每个线程都有一个局部引用表,在正确的线程中使用JNIEnv*就是至关重要的了。




你可能感兴趣的:(Exception)