一、实现思路:
1、jni里面调用java方法的大致步骤是:根据jobject获取jclass(静态方法就不用这一步了)--> 获取jmethodid --> 调用方法。 2、jni里面调用java方法的环境分为2种。 第一种:在env所在线程调用java方法,这种情况不需要做特殊处理,直接按照步骤执行即可。 第二种:在pthread子线程调用java方法,这种情况下就需要做处理了。在jni中,子线程中是不能直接调用JNIEnv对象的,也不能直接调用env线程中的jobject对象,因为:jni中,JNIEnv是和线程相关的,每一个native方法所在线程就有一个当前线程相关的JNIEnv对象,而pthread线程中是不能调用native方法所在线程的JENnv对象的,解决办法是:利用JavaVM虚拟机,JavaVM是和进程相关的,一个进程里面的JavaVM都是同一个,所以在pthread线程中就可以通过JavaVM来获取(AttachCurrentThread)当前线程的JNIEnv指针,然后就可以使用JNIEnv指针操作数据了;还有在pthread线程中调用jobject对象时,首先需要把native线程里面的jobject创建全局引用(env->NewGlobalRef(jobj)),其返还的jobject对象就可以在程序中使用了。 3、在JNI_OnLoad中获取我们需要的JavaVM指针。
二、代码实现:
2.1、创建文件WlListener.cpp来统一管理回调方法,并在构造方法中传入所需参数。
头文件WlListener.h
// // Created by ywl5320 // #pragma once #ifndef JNITHREAD_WLLISTENER_H #define JNITHREAD_WLLISTENER_H #include <jni.h> class WlListener { public: JavaVM* jvm;//java虚拟机 _JNIEnv *jenv;//native线程env对象 jobject jobj;//全局对象 jmethodID jmid;//java 方法id,可以根据实际情况创建多个。 public: WlListener(JavaVM* vm, _JNIEnv *jenv, jobject obj); ~WlListener(); void onError(int type, int code, const char *msg); }; #endif //JNITHREAD_WLLISTENER_H
cpp文件WlListener.cpp
// // Created by ywl5320 // #include "WlListener.h" #include "WlAndroidLog.h" WlListener::WlListener(JavaVM *vm, _JNIEnv *env, jobject obj) { jvm = vm; jenv = env; jobj = obj; jclass clz = env->GetObjectClass(jobj); if(!clz) { LOGE("get jclass wrong"); return; } jmid = env->GetMethodID(clz, "onError", "(ILjava/lang/String;)V"); if(!jmid) { LOGE("get jmethodID wrong"); return; } } /** * * @param type 0:env线程 1:子线程 * @param code * @param msg */ void WlListener::onError(int type, int code, const char *msg) { if(type == 0) { jstring jmsg = jenv->NewStringUTF(msg); jenv->CallVoidMethod(jobj, jmid, code, jmsg); jenv->DeleteLocalRef(jmsg); } else if(type == 1) { JNIEnv *env; jvm->AttachCurrentThread(&env, 0); jstring jmsg = env->NewStringUTF(msg); env->CallVoidMethod(jobj, jmid, code, jmsg); env->DeleteLocalRef(jmsg); jvm->DetachCurrentThread(); } }
其中:jvm参数是为了获取子线程中的JNIEnv;jenv参数是native线程中的,在native线程中使用;jobj是全局对象;jmid是要调用的java层的方法id,还可以有其他方法。
2.2、通过JNI_OnLoad获取JavaVM: JavaVM* jvm; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void* reserved){ JNIEnv *env; jvm = vm; if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!=JNI_OK){ return -1; } return JNI_VERSION_1_6; }
JNI_OnLoad方法是在加载完.so库时就会自动调用的,所以在这里获取JavaVM是最佳的时机。
2.3、主线程和子线程调用回调方法 pthread_t callbackThread; void *callBackT(void *data) { //获取WlListener指针 WlListener *wlListener = (WlListener *) data; //在子线程中调用回调方法 wlListener->onError(1, 200, "Child thread running success!"); pthread_exit(&callbackThread); } extern "C" JNIEXPORT void JNICALL Java_com_ywl5320_jnithread_JniThread_callbackThread(JNIEnv *env, jobject jobj) { WlListener *wlListener = new WlListener(jvm, env, env->NewGlobalRef(jobj)); //在主线程中调用java方法 wlListener->onError(0, 100, "JNIENV thread running success!"); //开启子线程,并把WlListener指针传递到子线程中 pthread_create(&callbackThread, NULL, callBackT, wlListener); } 2.4、java方法 //3、回调线程 public native void callbackThread(); private OnErrorListener onErrorListener; public void setOnErrorListener(OnErrorListener onErrorListener) { this.onErrorListener = onErrorListener; } //Jni调用此方法,把结果返回到java层 public void onError(int code, String msg) { if(onErrorListener != null) { onErrorListener.onError(code, msg); } } public interface OnErrorListener { void onError(int code, String msg); }
这样就完成了在C/C++中不同线程回调java方法了。