您的位置:首页 > 编程语言 > C语言/C++

android multithread in c/c++ to call JNI

2014-01-02 15:06 288 查看
转载:请注明原地址: /article/10219771.html

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 calling
pthread_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. Any
FindClass
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 的第二篇/article/10219772.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐