简体   繁体   English

JNI错误:访问过时的弱全局引用

[英]JNI Error: accessed stale weak global reference

I cache a reference to a Java object in my native code, just like this: 我在本机代码中缓存对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;
    }
}

and I access it in another native 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());
    }
}

This code run onto Android 4.3. 此代码运行在Android 4.3上。 Why do I get this error and how can I fix it? 为什么我会收到此错误以及如何解决?

OK, I've solved this problem! 好的,我已经解决了这个问题! Actually I cached _JAVA_ENV and use it mistakenly. 实际上我缓存了_JAVA_ENV并错误地使用它。 From this blog I found : 从这个博客我发现:

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. 虽然任何给定的JNIEnv *仅在一个线程上有效,但因为Android在JNIEnv *中从未有任何每线程状态,所以过去可能会在错误的线程上使用JNIEnv *。 Now there's a per-thread local reference table, it's vital that you only use a JNIEnv* on the right thread. 现在有一个每线程的本地参考表,至关重要的是你只在正确的线程上使用JNIEnv *。

So I thought there is no problem that I cache the JNIEnv and use it in one thread, but actually the JNIEnv was stale when the program get into java environment and returned to native environment. 所以我认为没有问题,我缓存JNIEnv并在一个线程中使用它,但实际上当程序进入java环境并返回到本机环境时, JNIEnv是陈旧的。 (eh... forgive my poor English) (呃...原谅我可怜的英语)

And from the documentation , I found : 文档中 ,我发现:

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. 如果一段代码没有其他方法来获取它的JNIEnv,你应该共享JavaVM,并使用GetEnv来发现线程的JNIEnv。

So, you should cache the JavaVM, and use it to get JNIEnv , the code would be like this : 所以,你应该缓存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;
}

Hope can help someone! 希望可以帮助别人! (please forgive my poor English again..) (请再次原谅我可怜的英语..)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM