解决 Android 4.0 新的 GC 机制导致的 JNI 异常

今天更新了 Android SDK ,然后在维护 AND-WebP 项目的时候报了一个 JNI 异常:

JNI ERROR (app bug): accessed stale local reference 0xe7b00025 (index 9 in a table of size 8)

之前版本的 Android ,虽然使用的是直接指针,但是 GC 不会随便回收对象,因此一般不会出问题。当然这样其实是很不安全的。

但从 ICS (4.0) 开始,GC 机制有所改变:全局对象使用过程中可能会被回收。不同于之前的使用直接指针,现在是通过一个 handle (指针的引用)去操作。当 GC 回收对象时,只需要修改这张 handle 表即可,而表的维护工作显然交给底层就好了。因此,这个「改变」其实应该说是「改进」,这样操作全局对象更安全了。

在应用层很多地方用到了 Observer 机制,只要数据有改变就会通过一个 onChanged() 回调去通知引用者。但是 JNI 层显然没有这种机制,为了防止发生不可预知的错误,系统只能用抛异常的方式去提醒开发者修改 JNI 层不安全的代码。

那么,具体该怎么解决这个问题呢?

首先想到的就是直接修改 AndroidManifest.xml 中的 targetSdkVersion 了,设置为 14 以下即可。

这个最简单,但显然不是最合适的方式,新的 API 迟早得用的。当然如果 JNI 层不是自己写的而是直接用的 so ,那也只能这样了。

推荐的方式是修改 JNI 层代码原来直接通过 FindClass() 函数得到 static 对象的地方:

jclassRef = jniEnv->FindClass("android/graphics/BitmapFactory$Options");

改为通过调用 NewGlobalRef() 函数得到全局变量的引用,然后强制转换下类型:

jclass tmpClass = jniEnv->FindClass("android/graphics/BitmapFactory$Options");
jclassRef = (jclass)jniEnv->NewGlobalRef(tmpClass);

经过以上修改,用最新的 Android 4.4 编译,targetSdkVersion 设置为 19 ,运行不会再报错。

更多相关内容参考这篇文章