jni层调试线程死机原因
一,导致死机原因:
一个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保存起来。(转录)
线程是执行的单元,同一个进程内的多个线程共享了进程的地址空间,线程一般有自己的栈,但是如果想要实现某个全局变量在不同的线程之间取不同的值,而且不受影响。一种办法是采用线程的同步机制,如对这个变量的读写之处加临界区或者互斥量,但是这是以牺牲效率为代价的,能不能不加锁呢?线程局部存储就是干这个的。
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,,(LPTHREAD_START_ROUTINE)ThreadFun,,NULL,NULL); }
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() { Sleep(); //这里改变JAVA对象的属性值(回调JAVA) env->SetIntField(gs_object,fieldPtr,(jint)gs_i++); } }
三,我们在网上看到有些关于jni的程序资料用了(*env)->;
我们需要谨记:在linux下如果.c文件中用 “env->” 编译会找不到此结构,必须用“(*env)->”,或者改成.cpp文件,以 c++的方式来编译。