1. 程式人生 > >定製Android系統開發之八——實現從JNI到Java的回撥

定製Android系統開發之八——實現從JNI到Java的回撥

前面已經實現了APP->xxxManager->xxxManagerService->jni的函式呼叫,這篇博文就來實現jni->xxxManagerService的回撥。

使用環境

我先說一下我的應用環境吧。我在有一個對裝置節點進行輪詢的執行緒,當能讀到訊息的時候就呼叫jni中的一個回撥函式。回撥函式會呼叫java中的函式來傳遞訊息。

函式定義

JNI中回撥函式的宣告如下:

get_msg_cb(message *evtmsg)

它的引數是一個結構體,我在回撥函式裡將解析這個結構體,轉化成int陣列。再呼叫下面的Java函式:

private
void recvMsgFromNative(int[] msg)

JNIEnv的物件

但凡做過JNI開發,都會知道,在呼叫JNI函式的時候,一個JNIEnv指標是必須具備的。在從Java呼叫的JNI函式的引數列表裡面,第一個引數就是一個JNIEnv指標。但是對於我的get_msg_cb()函式,是從底層回撥的,沒有這個JNIEnv指標,該怎麼辦呢?

在JNI中,JNIEnv指標是與執行緒一一對應的,也就是說,每個執行緒都有一個JNIEnv,線上程一里不能使用執行緒二的JNIEnv。所以在register_android_server_iflytek_radiomanagerservice裡面把JNIEnv儲存在一個全域性變數中,然後在get_msg_cb()函式裡面使用,是不行的。但是,有一個物件卻是全域性的,那就是JavaVM。我們可以將JavaVM儲存在全域性變數中,然後通過JavaVM在get_msg_cb()裡面獲取JNIEnv指標。

static JavaVM * gJavaVM;


int register_android_server_radiomanagerservice(JNIEnv* env) {

    // 此處省略很多行

    // 獲取JavaVM指標,儲存在全域性變數裡面。
    env->GetJavaVM(&gJavaVM);

    return 0;
}

獲取物件和函式的引用

Java中的非static函式是屬於某個物件的,要想呼叫這個函式,就必須擁有這個物件的引用。我在Java裡面定義了一個nativeInit()函式,並按照上一篇博文的方法對映到jni中的nativeInit()函式。在RadioManagerService()的建構函式裡呼叫nativeInit()函式。在NativeInit()函式裡面,通過JNIEnv和jobject獲取了RadioManagerService物件的例項並儲存在了全域性變數中。同時還獲取了recvMsgFromNative()函式的ID並儲存在全域性變數中。程式碼如下:

static jobject gRadioManagerServiceObj;
static jclass gRadioManagerServiceClass;

static void nativeInit(JNIEnv* env, jobject obj) {
    ALOGD("nativeInit() is called");

    mcu_rpc_init();

    // 獲取UartManagerService物件的例項
    gRadioManagerServiceObj = env->NewGlobalRef(obj);

    // Callbacks
    FIND_CLASS(gRadioManagerServiceClass,
            "com/android/server/RadioManagerService");

    GET_METHOD_ID(gRadioManagerServiceClassInfo.recvMsgFromNative,
            gRadioManagerServiceClass, "recvMsgFromNative", "([I)V");

    // 註冊回撥
    mcu_rpc_set_callback(get_msg_cb);
}

JNI函式的實現

下面就是JNI函式的實現了。這裡直接給出程式碼:

void get_msg_cb(message *evtmsg) {
    ALOGD("get_msg_cb() is called");
    int i;

    // 通過JavaVM獲取JNIEnv指標
    JNIEnv* env;
    gJavaVM->AttachCurrentThread(&env, NULL);

    if (env == NULL) {
        ALOGD("JNIEnv is null");
        return;
    }

    // 從message結構體獲取陣列
    jintArray returnArray = getArrayFromMsg(env, evtmsg);

    // 呼叫Java中的函式
    if (gRadioManagerServiceObj != NULL) {
        env->CallVoidMethod(gRadioManagerServiceObj,
                gRadioManagerServiceClassInfo.recvMsgFromNative, returnArray);
    }

    // 這裡的returnArray是在getArrayFromMsg()函式中通過env->NewIntArray()新建出來的,所以必須delete掉,以釋放空間,不然會有記憶體洩露
    env->DeleteLocalRef(returnArray);

    checkAndClearExceptionFromCallback(env, __FUNCTION__); 
}