android multithread in c/c++ to call JNI

况嘉运
2023-12-01

转载:请注明原地址: http://blog.csdn.net/wu4long/article/details/17756419

    

android的c/c++调用java的代码 都是通过jni的。

但如果你在c/c++新建自己的线程,然后在线程上通过jni调用java的代码,那就麻烦来了。 找不到你需要调用的class。

怎么办?


Android里面有说明,http://developer.android.com/training/articles/perf-jni.html 。

造成这个原因是:

You can get into trouble if you create a thread yourself (perhaps by callingpthread_create and then attaching it with AttachCurrentThread). Now the stack trace looks like this:

    dalvik.system.NativeStart.run(Native Method)

The topmost method is NativeStart.run, which isn't part of your application. If you call FindClass from this thread, the JavaVM will start in the "system" class loader instead of the one associated with your application, so attempts to find app-specific classes will fail.

也就是说,当前java的VM的线程的CallStack是
 dalvik.system.NativeStart.run(Native Method)

JavaVM将使用系统的class loader而不是你的应用使用的class loader. 从而去找应用自身的类,将会失败。


android也给出了几个解决方案:

There are a few ways to work around this:


  • Do your FindClass lookups once, in JNI_OnLoad, and cache the class references for later use. AnyFindClass calls made as part of executing JNI_OnLoad will use the class loader associated with the function that called System.loadLibrary (this is a special rule, provided to make library initialization more convenient). If your app code is loading the library, FindClass will use the correct class loader.
  • Pass an instance of the class into the functions that need it, by declaring your native method to take a Class argument and then passing Foo.class in.
  • Cache a reference to the ClassLoader object somewhere handy, and issue loadClass calls directly. This requires some effort.

第一种方法就是通过在本来的java线程调用c/c++的时候,来获取相应的jclass,然后缓存起来。
一般做法就是在JNI_OnLoad方法处理。 这个是有局限的。如果你的类不多,这样处理还是可行的。
第二种方法,网上的例子比较多,就是在本来的java线程中调用自定义的native函数。从而在native中缓存此对象。然后在新建的线程里面通过此对象来获取相应的class。 这个方法和上面的类似。如果类过多,就需要多个此对象。
网上有此代码。不过有些地方描述不清楚。我这边总结一下:
Java的代码
void setJNIEnv();
在应用开始的地方,调用此方法,一般是Activity的onCreate。
在c/c++端native文件中:


JavaVM *g_jvm = NULL;
jobject g_obj = NULL;
JNIEXPORT void Java_YOURCLASS_setJNIEnv( JNIEnv* env, jobject obj)
 {
     (*env)->GetJavaVM(env,&g_jvm);
     g_obj = (*env)->NewGlobalRef(env,obj);
 }
这样就建立起了全局的object。
然后在你的c/c++的线程函数中:
in your thread fund:
          

     JNIEnv* env = NULL;

           if( g_jvm->AttachCurrentThread(&env,NULL) < 0)
     {
        /// your error process.
     }
           jclass cls = (*env)->GetObjectClass(env,g_obj);
          if(cls == NULL)
          {
              ///your error process.
             
          }
          ///获取到jclass, 就可以调用此class的方法了。
          /// GetMethodID 和  CallVoidMethod 等各种jni方法可以调用。 class参数将cls放入。
         …….

 在你的线程结束的地方:调用

     g_jvm->DetachCurrentThread();

既:一个native线程,开始必须调用 AttachCurrentThread. 结束调用 DetachCurrentThread。 
不建议多次调用   AttachCurrentThread / DetachCurrentThread。否则可能会造成虚拟机的内存泄漏问题。
因为调用 AttachCurrentThread 是java虚拟机要创建java端的线程与之对应。 这个开销大家自己去想吧。呵呵。因此,一个线程just call one time。

从上面的过程来看,它回避了 env->FindClass的问题。
哪有没有更好的方法呢?
差不多就是android的第三种方法,通过ClassLoader object来处理了。


   
这里写的过多了,新开第二篇来描述ClassLoader的方法。

android multithread in c/c++ to call JNI 的第二篇: http://blog.csdn.net/wu4long/article/details/17757433

           






 类似资料: