JNI DETECTED ERROR IN APPLICATION解決記錄

       最近遇到一個JNI的問題,同一套代碼在Android4.4版本前的設備上運行是OK的,但是在Android5.0之後的設備上就會崩潰,查看logcat發現報JNI DETECTED ERROR IN APPLICATION錯誤。

      (1)第一個錯誤:

               JNI DETECTED ERROR IN APPLICATION: calling static method xxxx with CallxxxxMethodV in call to CallxxxxMethodV

       有過JNI編程經驗的就會知道,調用方法分static和非static兩種,分別會用到

                    GetStaticMethodID                                    GetMethodID    

                    CallStatic<type>Method                            Call<type>Method

                    其中<type>指Int, Long, Char等類型

       問題的原因就是編寫JNI代碼的同事混用了這兩種方式,先用了GetStaticMethodID,然後又用的非static的CallIntMethod方法,Android4.4之前版本JNI檢查機制沒有Android5.0之後的版本嚴格,所以沒有報錯,程序也不會崩潰,但正確的方式應該是GetMethodID+Call<type>Method,GetStaticMethodID+CallStatic<type>Method,這個問題解決後又接着又報出了下面的錯誤。

      (2)第二個錯誤:

               JNI DETECTED ERROR IN APPLICATION: native code passing in reference to invalid stack indirect reference table or invalid reference: 0xfff5f1b0
               in call to CallVoidMethod
 <==這一行報錯是問題的切入點

       這個錯誤顯示是CallVoidMethod的參數非法引用,網上找了一些相關問題的帖子,結合本地代碼,發現最有可能的就是線程間不能直接傳遞JNIEnv和jobject這類線程專屬屬性值導致,要知道JavaVM是屬於java進程的,每個進程只有一個JavaVM,而這個JavaVM可以被多線程共享,但是JNIEnv和jobject是屬於線程私有的,不能共享,那有什麼辦法解決呢?這裏我們不講JNIEnv的線程間傳遞,有興趣的網上可以找到相關帖子,而jobject可以用全局引用的方式在多線程間使用,舉個簡單的例子:

#Include <jni.h>
#include <pthread.h>
pthread_t pthread;
jobject object;
JavaVM* jvm;
void *run(void *arg)
{
    JNIEnv * jenv;
    jvm->AttachCurrentThread(&jenv, NULL);
    jenv->CallVoidMethod(object, jnv->GetMethodID(jnv->GetObjectClass(object), "foo", "()V"));
    jenv->DeleteGlobalRef(object);
    jvm->DetachCurrentThread();
    return NULL;
}  

void Java_com_program_Initialize(JNIEnv*jenv, jobject caller)
{   /*要想在新線程中使用對象caller,就必須以全局引用方式保存,否則caller只是局部引用,本方法返回後就會銷燬*/
    object = jnv->NewGlobalRef(caller); 
    jenv->GetJavaVM(&jvm);
    pthread_create(&thread, NULL, run, NULL);
}
           再回到前面的問題,按照這個線索,我在本地JNI代碼裏發現在pthread_create創建新線程之前,僅僅將jobject保存在了一個全局變量裏面,而沒有使用全局引用,以上面的代碼爲例,即本地JNI代碼裏進行了類似object = caller;的賦值,這顯然是沒有用的,一旦函數返回,caller就會被GC回收銷燬,object指向的就是一個非法地址,最終導致上面的JNI錯誤。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章