android multithread in c/c++ to call JNI

转载:请注明原地址: 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

           






  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
一个android平台上的 扩展任务库,在AsyncTask基础上进行扩展。 用法 1.继承 com.github.snowdream.android.util.concurrent.AsyncTask //inherit a class from com.github.snowdream.android.util.concurrent.AsyncTask public class DownloadFilesTask extends AsyncTask { public DownloadFilesTask(TaskListener listener) { //explicit inherit the construction from the super class. super(listener); } /** * TODO * if error occurs,carry it out. * * if (listener != null) { * listener.onError(new Throwable()); * } */ protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i ) { totalSize = 10; publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } return totalSize; } } 2.定义一个 TaskListener.其中的第一个泛型参数是返回结果类型,第二个泛型参数是任务进度的类型。 private TaskListener listener = new TaskListener(){ @Override public void onStart() { super.onStart(); Log.i("onStart()"); } @Override public void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); Log.i("onProgressUpdate(values)" values[0] ); } @Override public void onSuccess(Long result) { super.onSuccess(result); Log.i("onSuccess(result)" result); } @Override public void onCancelled() { super.onCancelled(); Log.i("onCancelled()"); } @Override public void onError(Throwable thr) { super.onError(thr); Log.i("onError()"); } @Override public void onFinish() { super.onFinish(); Log.i("onFinish()"); } }; 3.创建一个AsyncTask任务,并且执行。 URL url = null; try { url = new URL("http://www.baidu.com/"); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } new DownloadFilesTask(listener).execute(url,url,url); 标签:android

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值