jni層調試線程死機原因
一,導致死機原因:
jni層中線程函數中只要添加調用env的函數,,就會死機 二,解決方法第一我們應該理解:①(獨立性) JNIEnv 是一個與線程相關的變量,即線程A有一個 JNIEnv變量, 線程B也有一個JNIEnv變量,由于線程相關,所以A線程不能使用B線程的 JNIEnv 結構體變量。那么如何保證了每個線程JNIEnv的獨立性呢?一個java對象通過JNI調用DLL中一個send()函數向服務器發送消息,不等服務器消息到來就立即返回,同時把JNI接口的指針JNIEnv *env(虛擬機環境指針),和jobject obj保存在DLL中的變量里.一段時間后,DLL中的消息接收線程接收到服務器發來的消息,并試圖通過保存過的env和obj來調用先前的java對象的方法(相當于JAVA回調方法)來處理此消息此時程序會突然退出(崩潰).
即前臺JAVA線程發送消息,后臺線程處理消息,歸屬于兩個不同的線程,不能使用相同的JNIEnv變量,這里可以利用一個機制: 利用全局的 JavaVM * 指針得到當前線程的 JNIEnv* 指針,與在C++中兩個線程使用TLS進行局部存儲類似的原理。
具體方法:
獲取全局的JavaVM變量:
/* Our VM */JavaVM *g_vm;
env->GetJavaVM(&g_vm); //來獲取JavaVM指針.獲取了這個指針后,將該JavaVM保存起來。(轉錄)
②(公共性) 先了解TLS(thread-local storage)線程是執行的單元,同一個進程內的多個線程共享了進程的地址空間,線程一般有自己的棧,但是如果想要實現某個全局變量在不同的線程之間取不同的值,而且不受影響。一種辦法是采用線程的同步機制,如對這個變量的讀寫之處加臨界區或者互斥量,但是這是以犧牲效率為代價的,能不能不加鎖呢?線程局部存儲就是干這個的。
解決以上兩個問題:1首先定義全局變量namespace android{ static JavaVM* gJavaVM = NULL; //定義一個全局Java VM引用對象 static jobject gJavaObj = NULL; //定義一個全局Java object對象,對于java層的類對象 ......
2其次在調用某個線程的函數中定義:(保證線程在進程中資源的公共性,這兩句是把參數傳給所開線程)
JNIEXPORT void JNICALL Java_Test_setEnev(JNIEnv *env, jobject obj) { env->GetJavaVM(&gs_jvm); //保存到全局變量中JVM //直接賦值obj到DLL中的全局變量是不行的,應該調用以下函數: gs_object=env->NewGlobalRef(obj); HANDLE ht=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ThreadFun,0,NULL,NULL); }3在線程函數中 引用: (保證線程函數 對應 env 和class)
void WINAPI ThreadFun(PVOID argv)//JNI中線程回調這個方法 { JNIEnv *env; gs_jvm->AttachCurrentThread((void **)&env, NULL); //對應這幾句說白了就是從上面函數中把變量取出來在該線程中使用 jclass cls = env->GetObjectClass(gs_object); //獲取JAVA線程中的全局對象 jfieldID fieldPtr = env->GetFieldID(cls,"value","I"); // 獲取JAVA對象 while(1) { Sleep(100); //這里改變JAVA對象的屬性值(回調JAVA) env->SetIntField(gs_object,fieldPtr,(jint)gs_i++); } }
三,我們在網上看到有些關于jni的程序資料用了(*env)->;
我們需要謹記:在linux下如果.c文件中用 “env->” 編譯會找不到此結構,必須用“(*env)->”,或者改成.cpp文件,以 c++的方式來編譯。
------------------------------------------------------------------------新聞熱點
疑難解答