我在本机代码中缓存对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 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我发现:

尽管任何给定的JNIEnv *仅在一个线程上有效,但由于Android在JNIEnv *中从来没有任何按线程状态,因此过去可以避免在错误的线程上使用JNIEnv *。现在有一个每个线程的本地引用表,至关重要的是,您只能在正确的线程上使用JNIEnv *。

因此,我认为缓存JNIEnv并在一个线程中使用它没有问题,但是实际上,当程序进入Java环境并返回到本机环境时,JNIEnv是过时的。 (eh ...原谅我可怜的英语)

documentation中,我发现:

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

希望可以帮助到某人! (请再次原谅我的英语不好。)

10-04 21:27