Android-Jni線程(三)— JNI全局回調java方法

 

我的視頻課程(基礎):《(NDK)FFmpeg打造Android萬能音頻播放器》

我的視頻課程(進階):《(NDK)FFmpeg打造Android視頻播放器》

我的視頻課程(編碼直播推流):《Android視頻編碼和直播推流》

 

目錄:

        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

 

//
// 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方法了。

 

源碼下載:Github:Android-JniThread 歡迎star

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章