標籤:
概述
在開發framework的時候有時會遇到需要自己開發JNI,以便使Java能夠調用自己底層開發的庫。網上的文章一般都是介紹如何通過命名規則及javah,使jni層函數與java層函數自動建立連結(Java虛擬機器通過命名規則建立),本文將講解如何動態註冊jni函數。
依賴庫及標頭檔
先貼出Android.mk的代碼
1 LOCAL_PATH :=$(call my-dir) 2 include $(CLEAR_VARS) 3 4 LOCAL_SRC_FILES := kiki_jni.cpp 5 kiki_local.cpp 6 7 LOCAL_SHARED_LIBRARIES := libandroid_runtime 8 libnativehelper 9 liblog 10 11 #dependence12 LOCAL_REQUIRED_MODULES := 13 14 LOCAL_C_INCLUDES += $(JNI_H_INCLUDE) 15 16 #add micro define17 LOCAL_CFLAGS += 18 19 LOCAL_MODULE := liblearnjni_jni20 21 include $(BUILD_SHARED_LIBRARY)
LOCAL_SHARED_LIBRARIES:需要的共用庫,在android中我們用刀libandroid_runtime及libnativehelper兩個。
LOCAL_C_INCLUDES:需要的標頭檔包含,寫$(JNI_H_INCLUDE)即可。
在Android的JNI架構中,一般會有一個JNI的cpp檔案用於溝通上層Java與底層Cpp,具體的商務邏輯實現會放在底層Cpp的另一個檔案中,因此這裡用kiki_jni.cpp類比JNI檔案橋,kiki_local.cpp類比實際實現邏輯的檔案。
實際使用的標頭檔主要有:
#include <jni.h>#include <JNIHelp.h>#include "android_runtime/AndroidRuntime.h"#include "android_runtime/Log.h"
JNI註冊流程
JNI註冊流程其實很簡單,主要分為三個步驟:
1.實現JNI本地函數
2.通過JNINativeMethod機構體將Java函數與本地函數一一關聯,實現映射表
3.調用AndroidRuntime::registerNativeMethods方法註冊函數映射表
那麼什麼時候註冊函數映射表呢?一般實現在jint JNI_OnLoad(JavaVM* vm, void* reserved)函數中。這個函數會在動態庫被載入後第一時間執行,因此一般會在裡面做一些初始化操作。具體代碼如下:
1 using namespace android; 2 3 static const char* const kLearnJni = "android/kiki/LearnJni"; 4 5 static jint 6 android_kiki_LearnJni_helloJni(JNIEnv *env, jobject thiz, jstring ip, jint port) { 7 return getMagicNum(); 8 } 9 10 static JNINativeMethod gMethods[] = { 11 { 12 "helloJni",13 "(Ljava/lang/String;I)I",14 (void *)android_kiki_LearnJni_helloJni15 }, 16 };17 18 int register_android_kiki_LearnJni(JNIEnv *env) {19 return AndroidRuntime::registerNativeMethods(env, kLearnJni, gMethods, NELEM(gMethods));20 }21 22 jint JNI_OnLoad(JavaVM* vm, void* reserved) {23 JNIEnv *env = NULL;24 jint res = -1; 25 26 if(vm -> GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {27 ALOGE("ERROR: Get java env failed");28 goto bail;29 } 30 31 if(register_android_kiki_LearnJni(env) <= 0) {32 ALOGE("ERROR: LearnJni native registration failed");33 goto bail;34 35 } 36 37 res = JNI_VERSION_1_4;38 ALOGD("JNI library onload success");39 40 bail:41 return res;42 }
其他資訊
每個Java進程有一個JavaVM,每個執行線程有一個JNIEnv,可以通過JavaVM擷取當前JNIEnv。JNIEnv的本質就是提供出來的JNI操作Java對象的方法和成員變數的API介面類。JNIEnv通過getMethod及get<Type>Field方法獲得Java對象的方法及成員變數,通過對應的call<Type>Method方法及set<Type>Field方法操作他們。
Java函數在JNI中籤名對應如下:
比較特殊的就是boolean轉為Z,long轉為J。
Android JNI開發