Android-Jni线程(三)— JNI全局回调java方法

2,462 阅读8分钟
原文链接: blog.csdn.net

目录:

        Android-Jni线程(一)— 创建线程

        Android-Jni线程(二)— 线程锁之生产者消费者

        Android-Jni线程(三)— JNI全局回调java方法


        在Android中用C/C++开发一部分功能时,不可避免的会把状态告诉给java层,由java层来做相应的处理或展示。而C/C++要把信息传递给java层就会调用java层原先写好的回调方法,这样才能把信息传递给java层。当C/C++文件很多时,里面线程也很多时,这时就需要我们使用统一的回调函数来处理了,不然会很混乱的。今天就给出一种解决方法能实现不同文件里面、不同线程里面都可以回调java方法。还是先看看效果:



一、实现思路:

        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

[cpp] view plain copy print?
  1. //  
  2. // Created by ywl5320  
  3. //  
  4. #pragma once  
  5. #ifndef JNITHREAD_WLLISTENER_H  
  6. #define JNITHREAD_WLLISTENER_H  
  7.   
  8. #include <jni.h>  
  9.   
  10. class WlListener {  
  11.   
  12. public:  
  13.   
  14.     JavaVM* jvm;//java虚拟机  
  15.     _JNIEnv *jenv;//native线程env对象  
  16.     jobject jobj;//全局对象  
  17.     jmethodID jmid;//java 方法id,可以根据实际情况创建多个。  
  18.   
  19. public:  
  20.   
  21.     WlListener(JavaVM* vm, _JNIEnv *jenv, jobject obj);  
  22.     ~WlListener();  
  23.   
  24.     void onError(int type, int code,  const char *msg);  
  25.   
  26. };  
  27.   
  28.   
  29. #endif //JNITHREAD_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

[cpp] view plain copy print?
  1. //  
  2. // Created by ywl5320  
  3. //  
  4.   
  5. #include "WlListener.h"  
  6. #include "WlAndroidLog.h"  
  7.   
  8. WlListener::WlListener(JavaVM *vm, _JNIEnv *env, jobject obj) {  
  9.     jvm = vm;  
  10.     jenv = env;  
  11.     jobj = obj;  
  12.     jclass clz = env->GetObjectClass(jobj);  
  13.     if(!clz)  
  14.     {  
  15.         LOGE("get jclass wrong");  
  16.         return;  
  17.     }  
  18.     jmid = env->GetMethodID(clz, "onError", "(ILjava/lang/String;)V");  
  19.     if(!jmid)  
  20.     {  
  21.         LOGE("get jmethodID wrong");  
  22.         return;  
  23.     }  
  24. }  
  25.   
  26. /** 
  27.  * 
  28.  * @param type  0:env线程 1:子线程 
  29.  * @param code 
  30.  * @param msg 
  31.  */  
  32. void WlListener::onError(int type, int code, const  char *msg) {  
  33.     if(type == 0)  
  34.     {  
  35.         jstring jmsg = jenv->NewStringUTF(msg);  
  36.         jenv->CallVoidMethod(jobj, jmid, code, jmsg);  
  37.         jenv->DeleteLocalRef(jmsg);  
  38.     }  
  39.     else if(type == 1)  
  40.     {  
  41.         JNIEnv *env;  
  42.         jvm->AttachCurrentThread(&env, 0);  
  43.   
  44.         jstring jmsg = env->NewStringUTF(msg);  
  45.         env->CallVoidMethod(jobj, jmid, code, jmsg);  
  46.         env->DeleteLocalRef(jmsg);  
  47.   
  48.         jvm->DetachCurrentThread();  
  49.     }  
  50. }  
//
// 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:

[cpp] view plain copy print?
  1. JavaVM* jvm;  
  2.   
  3. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void* reserved){  
  4.     JNIEnv *env;  
  5.     jvm = vm;  
  6.     if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!=JNI_OK){  
  7.         return -1;  
  8.     }  
  9.     return JNI_VERSION_1_6;  
  10. }  
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、主线程和子线程调用回调方法

[cpp] view plain copy print?
  1. pthread_t callbackThread;  
  2.   
  3. void *callBackT(void *data)  
  4. {  
  5.     //获取WlListener指针  
  6.     WlListener *wlListener = (WlListener *) data;  
  7.     //在子线程中调用回调方法  
  8.     wlListener->onError(1, 200, "Child thread running success!");  
  9.     pthread_exit(&callbackThread);  
  10. }  
  11.   
  12. extern "C"  
  13. JNIEXPORT void JNICALL  
  14. Java_com_ywl5320_jnithread_JniThread_callbackThread(JNIEnv *env, jobject jobj) {  
  15.     WlListener *wlListener = new WlListener(jvm, env, env->NewGlobalRef(jobj));  
  16.     //在主线程中调用java方法  
  17.     wlListener->onError(0, 100, "JNIENV thread running success!");  
  18.     //开启子线程,并把WlListener指针传递到子线程中  
  19.     pthread_create(&callbackThread, NULL, callBackT, wlListener);  
  20. }  
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方法

[java] view plain copy print?
  1. //3、回调线程  
  2.     public native void callbackThread();  
  3.   
  4.     private OnErrorListener onErrorListener;  
  5.   
  6.     public void setOnErrorListener(OnErrorListener onErrorListener) {  
  7.         this.onErrorListener = onErrorListener;  
  8.     }  
  9.   
  10.     //Jni调用此方法,把结果返回到java层  
  11.     public void onError(int code, String msg)  
  12.     {  
  13.         if(onErrorListener != null)  
  14.         {  
  15.             onErrorListener.onError(code, msg);  
  16.         }  
  17.     }  
  18.   
  19.     public interface OnErrorListener  
  20.     {  
  21.         void onError(int code, String msg);  
  22.     }  
//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方法了。


源码下载:Github:Android-JniThread  欢迎star