JNI Error: accessed stale weak global reference
JNI Error: accessed stale weak global reference
我在我的本机代码中缓存了对 Java 对象的引用,就像这样:
// java global reference deleter
// _JAVA_ENV is an instance of JNIEnv that is cached globally and just
// valid in current thread scope
static void g_java_ref_deleter(jobject ptr) {
_JAVA_ENV->DeleteGlobalRef(ptr);
}
// native class caches a java object reference
class NativeA {
private:
shared_ptr<_jobject> M_java_object;
public:
setJavaObject(jobject obj) {
M_java_object = shared_ptr<_jobject>(_JAVA_ENV->NewGlobalRef(obj), g_java_ref_deleter);
}
shared_ptr<_jobject> getJavaObject() const {
return M_java_object;
}
}
我在另一个本地访问它class:
class NativeB {
public:
void doSomething(NativeA& a) {
// here I got an error: accessed stale weak global reference
// make_record do something on java object (set float field actually)
make_record(a.getJavaObject().get());
}
}
此代码 运行 到 Android 4.3。为什么会出现此错误,我该如何解决?
好的,我已经解决了这个问题!实际上我缓存了 _JAVA_ENV
并错误地使用了它。从这个 blog 我发现:
Although any given JNIEnv* is only valid for use on one thread, because Android never had any per-thread state in a JNIEnv*, it used to be possible to get away with using a JNIEnv* on the wrong thread. Now there’s a per-thread local reference table, it’s vital that you only use a JNIEnv* on the right thread.
所以我认为我缓存 JNIEnv
并在一个线程中使用它没有问题,但实际上当程序进入 java 环境时 JNIEnv
已经过时并且回到原生环境。 (呃……原谅我蹩脚的英文)
并且从 documentation,我发现:
If a piece of code has no other way to get its JNIEnv, you should share the JavaVM, and use GetEnv to discover the thread's JNIEnv.
所以,你应该缓存JavaVM,并用它来获取JNIEnv
,代码是这样的:
JavaVM* g_jvm;
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
g_jvm = vm;
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
// Get jclass with env->FindClass.
// Register methods with env->RegisterNatives.
return JNI_VERSION_1_6;
}
JNIEnv* getJNIEnv() {
JNIEnv* env;
g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6 /*version*/);
return env;
}
希望能帮到人! (请再次原谅我糟糕的英语..)
我在我的本机代码中缓存了对 Java 对象的引用,就像这样:
// java global reference deleter
// _JAVA_ENV is an instance of JNIEnv that is cached globally and just
// valid in current thread scope
static void g_java_ref_deleter(jobject ptr) {
_JAVA_ENV->DeleteGlobalRef(ptr);
}
// native class caches a java object reference
class NativeA {
private:
shared_ptr<_jobject> M_java_object;
public:
setJavaObject(jobject obj) {
M_java_object = shared_ptr<_jobject>(_JAVA_ENV->NewGlobalRef(obj), g_java_ref_deleter);
}
shared_ptr<_jobject> getJavaObject() const {
return M_java_object;
}
}
我在另一个本地访问它class:
class NativeB {
public:
void doSomething(NativeA& a) {
// here I got an error: accessed stale weak global reference
// make_record do something on java object (set float field actually)
make_record(a.getJavaObject().get());
}
}
此代码 运行 到 Android 4.3。为什么会出现此错误,我该如何解决?
好的,我已经解决了这个问题!实际上我缓存了 _JAVA_ENV
并错误地使用了它。从这个 blog 我发现:
Although any given JNIEnv* is only valid for use on one thread, because Android never had any per-thread state in a JNIEnv*, it used to be possible to get away with using a JNIEnv* on the wrong thread. Now there’s a per-thread local reference table, it’s vital that you only use a JNIEnv* on the right thread.
所以我认为我缓存 JNIEnv
并在一个线程中使用它没有问题,但实际上当程序进入 java 环境时 JNIEnv
已经过时并且回到原生环境。 (呃……原谅我蹩脚的英文)
并且从 documentation,我发现:
If a piece of code has no other way to get its JNIEnv, you should share the JavaVM, and use GetEnv to discover the thread's JNIEnv.
所以,你应该缓存JavaVM,并用它来获取JNIEnv
,代码是这样的:
JavaVM* g_jvm;
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
g_jvm = vm;
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
// Get jclass with env->FindClass.
// Register methods with env->RegisterNatives.
return JNI_VERSION_1_6;
}
JNIEnv* getJNIEnv() {
JNIEnv* env;
g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6 /*version*/);
return env;
}
希望能帮到人! (请再次原谅我糟糕的英语..)