2015-06-23 5 views
3

я кэшировать ссылку на объект Java в моем родном коде, так же, как это:JNI Ошибка: доступ несвежий слабая глобальная ссылка

// 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. Почему я получаю эту ошибку и как ее исправить?

+0

Вам также необходимо удалить локальную ссылку на работу, которую вы получите в методе setJavaObject, хотя она не решит вашу ошибку. Просто чтобы ты знал. – 7383

+0

Вы уверены, что действительно передаете «jobject» везде, где это необходимо, а не какое-то другое значение? Кажется, вы не создаете каких-либо слабых глобальных ссылок, если вы не делаете это в какой-либо другой части вашего кода, которую вы нам не показали (?). 2 младших значащих бита «jobject» используются для определения того, какая у вас ссылка, а глобальная ссылка и слабая глобальная ссылка будут иметь разные значения в этих двух битах. – Michael

+0

@ 7383 Да, я делаю это по другой функции – Artiano

ответ

2

ОК, я решил эту проблему! На самом деле я кэшировал _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 и использовать его в одном потоке, но на самом деле JNIEnv был затхлым, когда программа попасть в среду Java и вернулась в родную среду , (Эх ... простите мой плохой английский язык)

И от 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; 
} 

Надежда может помочь кому-то! (пожалуйста, простите мой бедный английский снова ..)