Android JNI使用總結(二)
阿新 • • 發佈:2019-02-09
上一篇裡面主要是JNI中一些函式的介紹,這篇部落格就舉個例子來說明這些函式的使用方法。
專案介紹
這個例子來源於我實際的專案。這段程式碼的作用是:
- 通過Uart傳送訊息;
- 接受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);
}