首页 > 学院 > 开发设计 > 正文

NDK学习( 三),多线程与异步回调

2019-11-06 09:55:16
字体:
来源:转载
供稿:网友
场景:在java中传递任意类型参数,在JNI中新开子线程,将传入的参数处理后回调Java中的方法技术点:1、在JNI中新开子线程并传递参数2、在子线程中回调Java方法(误区:JNIEnv指针可以共享)对于技术点1,可参考一般的C++开发教程,一般创建子线程的方法有:通过pthread_createC++11中thread的用法参考:http://stackoverflow.com/questions/23872663/how-to-start-a-new-thread-from-jni另外要注意的是传递的参数应当是堆内存中的指针,或者是全局变量,对于局部变量,可能在子线程中调用时已经失效。对于技术点2,需要注意的是JNIEnv *这类指针是无法在线程间共享的,参考JNI文档。参考:http://stackoverflow.com/questions/13383823/native-multithreading-and-jni步骤:在JNI_OnLoad或者Java调用的native方法中缓存JavaVM*,JavaVM*指针是唯一在线程间可以共享的。在子线程的native方法中调用AttachCurrentThread ,获取JNIEnv*指针。通过JavaVM*和JNIEnv*找到你需要的jclasses,jobjects,jmethodIDs,它们不能在线程间共享。如果需要,将jclasses,jobjects转换为全局变量,jmethodIDs不需要转换,因为它不是对象。在JNI_OnUnlaod时删除全局变量这类引用,当你不再需要时。理解JNI_OnLoad、JNI_OnUnload参考:http://docs.Oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html#JNI_OnLoad当native lib被加载(如System.loadLibrary()执行后),虚拟机会执行JNI_OnLoad,该方法必须返回当前JNI版本号。如果native lib不exportJNI_OnLoad方法,虚拟机认为lib只需要JNI_VERSION_1_1.如果返回的版本号不能被识别,该lib将不能被加载。当包含native lib的类加载器被垃圾回收器回收,JNI_OnUnload会被调用。该方法可用来清理不再需要的指针。Java代码:public native void asyncCallBack(String str);//执行后向native lib传入字符串native代码:extern "C" {JavaVM* javaVM; //定义要全局共享的虚拟机指针jobject jinstance; //定义要共享的Java对象JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* jvm, void* reserved){javaVM = jvm; //将全局虚拟机指针赋值JNIEnv* env = NULL;jint result = -1;//获取JNI版本if (jvm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK){LOGE("GetEnv failed!");return result;}LOGE("JNI_VERSION:%d",JNI_VERSION_1_4);return JNI_VERSION_1_4;}//线程函数void* runAsync(void* args) {const char* chars = (const char*) args;JNIEnv *env = NULL;//Attach主线程if (javaVM->AttachCurrentThread(&env, NULL) != JNI_OK) {LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);return NULL;}if (env != NULL) {LOGE("jnienv* obtained");}else {return NULL;}LOGE("runAsync chars:%s", chars);jclass jclass1 = env->GetObjectClass(jinstance);if(jclass1==NULL){LOGE("sub thread : failed get class");return NULL;}else{LOGE("sub thread : find class");}jmethodID jmethodID =env->GetMethodID(jclass1,"callback","(Ljava/lang/String;)V");if(jmethodID==NULL){LOGE("sub thread : failed get jmethodID");return NULL;}else{LOGE("sub thread : find jmethodID");}string str =chars;string str2 = " from native";str.append(str2);jstring jstr=env->NewStringUTF(str.c_str());env->CallVoidMethod(jinstance,jmethodID,jstr);env->DeleteGlobalRef(jinstance);//删除全局引用if(javaVM!=NULL){javaVM=NULL;LOGE("release JavaVM*");}if(jinstance!=NULL){jinstance=NULL;LOGE("release jinstance*");}LOGE("sub thread:task finish");}JNIEXPORT void JNICALLJava_dev_mars_jnidemo_NativeThread_asyncCallBack(JNIEnv *env, jobject instance,jstring jstr) {const char* chars = env->GetStringUTFChars(jstr,0);LOGE("Java_dev_mars_jnidemo_NativeThread_asyncCallBack pass parameter:%s",chars);//将jobject转换为全局变量,因为jclass、jobject也不能在线程间共享jinstance = env->NewGlobalRef(instance);pthread_t thread_1;pthread_create(&thread_1,NULL,runAsync,(void*)chars);}}小结:jobject、jclass、jmethodID这些类型无法在线程间共享,需要转换为全局变量。本例中在JNI接口线程中将jobject转换为全局变量,在子线程通过JavaVM*获取JNIEnv*,并通过全局变量jobect获取jclass,从而得到jmethod。native调用java方法的函数主要有三种:参考:http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp16656NativeType Call<type>Method(JNIEnv *env, jobject obj,jmethodID methodID, ...);NativeType Call<type>MethodA(JNIEnv *env, jobject obj,jmethodID methodID, const jvalue *args);NativeType Call<type>MethodV(JNIEnv *env, jobject obj,jmethodID methodID, va_list args);通过观察发现,Method、MethodA、MethodV所带的参数类型不同,其中<type>是回调的返回类型。创建jobject的全局引用通过 env->NewGlobalRef(instance);回调完释放全局引用通过JNIEnv*的 env->DeleteGlobalRef(jinstance);参考:http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/sync.html如本文有任何问题欢迎指出,谢谢
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表