美文网首页JNI开发音视频开发
C++子线程调用Java方法

C++子线程调用Java方法

作者: 未见哥哥 | 来源:发表于2018-11-11 14:18 被阅读4次

    1. C++ 全局调用Java方法

    之前讨论过,如何C++主线程中调用 Java 函数C++主线程调用Java方法,下面来看看如何在子线程中调用 Java 函数。

    由于JNIEnv是与线程绑定的,就像 Android 的 Looper 也是和线程绑定一样,每一个 Looper 会对应一个线程。因此要在子线程中调用 Java 的方法,需要得到当前线程的 JNIEnv 实例。

    那么如何在获取当前线程的 JNIEnv 呢?

    1.1 获取当前线程的 JNIEnv

    因为JVM是进程相关的,所以可以通过JVM 来获取当前线程的JNIEnv,然后就可以调用java的函数了。

    • 获取 JVM 实例

    通过System.loadLibrary("xxx") 加载动态库时,系统就会回调JNI_OnLoad函数,因此我们只需要覆写这个函数,并且将JavaVM 指针保存到类成员变量即可。

    JNI_OnLoad(JavaVM* vm,void* reserved)
    
    • 通过JavaVM获取JNIEnv 实例

    下面演示在子线程获取 JNIEnv 的伪代码

    //1.根据 AttachCurrentThread 获取到当前线程的 JNIEnv 实例
    JNIEnv *env;
    
    vm->AttachCurrentThread(&env, 0);
    
    //2.调用 java 函数
    //call method
    
    //3.解除挂载当前线程
    vm->DetachCurrentThread();
    

    1.2 C++子线程调用Java方法

    下面是 native code 创建子线程调用 Java 层的 onSuccess 函数的流程图

    流程

    1.2.1 获取 JVM 实例

    在 native 代码中重写 JNI_OnLoad 得到 JavaVM 实例

    //定义一个全局 java vm 实例
    JavaVM *jvm;
    //在加载动态库时回去调用 JNI_Onload 方法,在这里可以得到 JavaVM 实例对象
    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;
    }
    

    1.2.2 JavaListener

    JavaListener 是在主线程创建,然后传递给子线程的。那么它是干什么用的?

    它会负责获取当前子线程的 JNIEnv 实例并且去调用 Java 层的 onSuccess 函数

    下面来看一下 JavaListener类是如何定义的?

    • 因为 JavaListener是一个 C++ 类,因此我们要先创建一个 JavaListener.h 头文件。

    JavaListener.h

    class JavaListener {
    //定义类成员属性
    public:
        JavaVM *vm;
        JNIEnv *env;
        jobject jobj;
        jmethodID jmethod;
    
    
    public:
        //定义构造函数
        JavaListener(JavaVM *vm, JNIEnv *env, jobject jobj);
    
        //析构函数
        ~JavaListener();
        //定义类成员方法 onSuccess 在这个函数会负责去调用 Java 层的 onSuccess 函数
        void onSuccess(const char *msg);
    };
    
    • 有了 JavaListener.h 文件之后,我们就可以开始定义对应的实现 JavaListener.cpp 文件。

    JavaListener.cpp

    #include <jni.h>
    #include "JavaListener.h"
    #include "android/log.h"
    
    #define LOGI(FORMAT, ...) __android_log_print(ANDROID_LOG_INFO,"liaowejian",FORMAT,##__VA_ARGS__);
    
    //在构造函数接收对应的参数
    JavaListener::JavaListener(JavaVM *vm, JNIEnv *env, jobject jobj) {
        this->vm = vm;
        this->env = env;
        this->jobj = jobj;
        
        jclass jclaz = env->GetObjectClass(jobj);
    
        //得到 jmethodid 实例
        jmethod = env->GetMethodID(jclaz, "onSuccess", "(Ljava/lang/String;)V");
    
    }
    
    //在子线程回调这个方法 onSuccess
    void JavaListener::onSuccess(const char *msg) {
        
        //得到子线程相关的 JNIEnv 实例
        JNIEnv *env;
        vm->AttachCurrentThread(&env, 0);
        
        //将需要传递给 Java 层 onSuccess 的 msg 转化为 jstring 实例
        jstring jmsg = env->NewStringUTF(msg);
    
        //调用 Java 层的函数
        env->CallVoidMethod(jobj, jmethod, jmsg);
        //删除本地引用 jmsg,避免内存泄露
        env->DeleteLocalRef(jmsg);
        //取消挂载线程
        vm->DetachCurrentThread();
    }
    

    1.2.3 创建C++子线程

    • 创建 JavaListener 对象
    • 创建子线程和childCallback 回调,它就相当于 Thread 中的 run 方法一样。然后将 JavaListener 对象传递给 childCallback
    • 在子线程中得到主线程传递过来的 JavaListener 实例
    • 通过JavaListener去执行对应的onSuccess函数。
    • 退出线程
    #include "JavaListener.h"
    pthread_t childThread;
    
    //线程执行体
    void *childCallback(void *data) {
        //3. 在子线程中得到主线程传递过来的 JavaListener 实例
        JavaListener *javaListener = (JavaListener *) data;
        //4. 通过 JavaListener 去执行对应的 onSuccess 函数。
        javaListener->onSuccess("hello from child thread");
        //5. 退出线程
        pthread_exit(&childThread);
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_com_example_lib_JniThreadDemo_callJavaMethodOnCppChildThread(JNIEnv *env, jobject instance) {
        //1. 传递给子线程的对象 JavaListener ,**这里需要 instance 转化为全局的实例**
        JavaListener *javaListener = new JavaListener(jvm, env, env->NewGlobalRef(instance));
        //2. 创建子线程,将 javaListener 传递给线程执行体 childCallback
        pthread_create(&childThread, NULL, childCallback, javaListener);
    }
    

    1.2.4 在 Java 层调用

    public void callJavaMethodOnCppChildThread(View view) {
        JniThreadDemo jniThreadDemo = new JniThreadDemo();
        jniThreadDemo.callJavaMethodOnCppChildThread();
    }
    

    上面演示了如何在子线程中回调 Java 层的函数。

    记录于 2018年11月11日 双十一 光棍节~

    相关文章

      网友评论

        本文标题:C++子线程调用Java方法

        本文链接:https://www.haomeiwen.com/subject/lavxfqtx.html