1. 程式人生 > >Android JNI使用總結(二)

Android JNI使用總結(二)

上一篇裡面主要是JNI中一些函式的介紹,這篇部落格就舉個例子來說明這些函式的使用方法。

專案介紹

這個例子來源於我實際的專案。這段程式碼的作用是:

  1. 通過Uart傳送訊息;
  2. 接受Uart傳送過來的訊息;

因為這裡的主要目的不是說明如何使用uart,所以我的這段程式碼中傳送是通過呼叫下面的方法實現的:

int uart_send(
    message *callmsg, // 待發送的訊息
    message *retmsg, // 傳送訊息之後的返回值
    unsigned timeout // 傳送超時
);

接受是通過註冊回撥函式實現的。當uart處理機接受到訊息之後,將呼叫我的回撥函式。回撥函式將訊息處理後返回給java層的程式碼。

  • 宣告幾個巨集。
#define FIND_CLASS(var, className) \
        var = env->FindClass(className); \
        LOG_FATAL_IF(! var, "Unable to find class " className);

#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
LOG_FATAL_IF(! var, "Unable to find method " methodName); #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ LOG_FATAL_IF(! var, "Unable to find field " fieldName);

這幾個巨集是我在framework/base/service/jni中找到的,挺好用的。

  • 宣告全域性變數。
static struct
{
    jmethodID recvMsgFromNative; // 這裡將儲存java類UartManagerService中的方法recvMsgFromNative的ID。
} gUartManagerServiceClassInfo;

static jobject gUartManagerServiceObj; // 這個物件將儲存UartManagerService物件的例項。
static jclass gUartManagerServiceClass; // 這個東西將儲存UartManagerService的class。
static JavaVM * gJavaVM; // 這個就是我在上一遍博文的最後提到的全域性的JavaVM物件,在必要的時候,將通過這個物件獲取JNIEnv指標。
  • 宣告native方法。
static JNINativeMethod gUartManagerServiceMethods[] =
{
    /* name, signature, funcPt */
    { "nativeTest", "()V", (void*) nativeTest },
    { "nativeInit", "()V", (void*) nativeInit }
};
  • 註冊native方法。
int register_android_server_iflytek_uartmanagerservice(JNIEnv* env)
{
    int res = jniRegisterNativeMethods(env,
                                       "com/android/server/iflytek/UartManagerService",
                                       gUartManagerServiceMethods, NELEM(gUartManagerServiceMethods) );
    if(res < 0)
        ALOGD("Unable to register native methods.");

    env->GetJavaVM(&gJavaVM);

    return 0;
}
  • 本地初始化。
static void nativeInit(JNIEnv* env, jobject obj)
{
    ALOGD("nativeInit() is called");


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

    // Callbacks
    FIND_CLASS(gUartManagerServiceClass, "com/android/server/iflytek/UartManagerService");

    GET_METHOD_ID(gUartManagerServiceClassInfo.recvMsgFromNative, gUartManagerServiceClass,
                  "recvMsgFromNative", "([I)V");

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

這個函式時在UartManagerService裡呼叫的,所以剛好可以用來獲取UartManagerService的例項。

  • 實現傳送程式碼。
static void nativeTest(JNIEnv* env, jobject obj)
{
    ALOGD("nativeTest() is called");
    message callmsg;
    message retmsg;

    // 迴圈傳送1000次
    for (int i = 0; i < 1000; ++ i)
    {
        // 填充callmsg
        // ……
        //

        uart_send(&callmsg, &retmsg, 200);
    }
}
  • 實現接收的回撥。

回撥函式的引數是message結構體,而java中的回撥函式的引數是int陣列,所以這裡要新建陣列並填內容。

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

    JNIEnv* env;
    gJavaVM->AttachCurrentThread(&env, NULL); // 這裡就是上一篇博文的最後提到的“在需要的時候”。因為這裡沒有JNIEnv指標,所以只能從gJavaVM中重新獲取。

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

    jintArray returnArray = env->NewIntArray(len);
    int * pArray = (int*)malloc(sizeof(int) * len);

    // 給本地陣列賦值
    pArray[0] = ……;
    pArray[1] = ……;
    for(i = 0; i < len; i++)
    {
        pArray[2+i] = evtmsg->data[i];
    }

    // 將值賦給返回值
    env->SetIntArrayRegion(returnArray, 0, len + 2, pArray);

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

    // 釋放記憶體
    free(pArray);
}