JNI error: accessing legacy weak global reference

I am caching a Java object reference in my own code like:

// 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 am accessing 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 runs on Android 4.3. Why am I getting this error and how can I fix it?

+3


source to share


1 answer


Ok, I solved this problem! Actually I was caching _JAVA_ENV

and using it wrongly. From this blog I found:

While any given JNIEnv * is only valid for use in one thread, because Android never had any state on a thread in JNIEnv *, it used to be possible to get away using JNIEnv * on the wrong thread. This is now a local lookup table for each thread, its vital that you use JNIEnv * on the right thread.

So, I thought there was no problem that I was caching JNIEnv

and using it on the same thread, but was actually JNIEnv

deprecated when the program got into the java environment and returned to the original environment. (uh ... sorry my poor english)

And from the documentation I found:



If a piece of code has no other way to get it JNIEnv, you should share JavaVM and use GetEnv to detect the JNIEnv stream.

So, you have to cache JavaVM and use it to get JNIEnv

, the code will be like this:

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 excuse my poor english again ..)

+2


source







All Articles