靜態(tài)注冊
原理:根據(jù)函數(shù)名來建立 java 方法與 JNI 函數(shù)的一一對應(yīng)關(guān)系预厌;
實現(xiàn)流程:
- 編寫 java 代碼樊零;
- 利用 javah 指令生成對應(yīng)的 .h 文件府怯;
- 對 .h 中的聲明進行實現(xiàn);
弊端:
- 編寫不方便玫镐,JNI 方法名字必須遵循規(guī)則且名字很長辅柴;
- 編寫過程步驟多箩溃,不方便;
- 程序運行效率低碌嘀,因為初次調(diào)用native函數(shù)時需要根據(jù)根據(jù)函數(shù)名在JNI層中搜索對應(yīng)的本地函數(shù)碾篡,然后建立對應(yīng)關(guān)系,這個過程比較耗時筏餐;
JNIEXPORT jstring JNICALL
Java_com_example_efan_jni_1learn2_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
動態(tài)注冊
原理:利用 RegisterNatives 方法來注冊 java 方法與 JNI 函數(shù)的一一對應(yīng)關(guān)系开泽;
實現(xiàn)流程:
- 利用結(jié)構(gòu)體 JNINativeMethod 數(shù)組記錄 java 方法與 JNI 函數(shù)的對應(yīng)關(guān)系;
- 實現(xiàn) JNI_OnLoad 方法魁瞪,在加載動態(tài)庫后穆律,執(zhí)行動態(tài)注冊;
- 調(diào)用 FindClass 方法导俘,獲取 java 對象峦耘;
- 調(diào)用 RegisterNatives 方法,傳入 java 對象旅薄,以及 JNINativeMethod 數(shù)組辅髓,以及注冊數(shù)目完成注冊泣崩;
優(yōu)點:
- 流程更加清晰可控;
- 效率更高洛口;
jstring stringFromJNI(JNIEnv *env, jobject thiz){
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
static const JNINativeMethod gMethods[] = {
{"stringFromJNI", "()Ljava/lang/String;", (jstring*)stringFromJNI}
};
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
__android_log_print(ANDROID_LOG_INFO, "native", "Jni_OnLoad");
JNIEnv* env = NULL;
if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) //從JavaVM獲取JNIEnv矫付,一般使用1.4的版本
return -1;
jclass clazz = env->FindClass("com/example/efan/jni_learn2/MainActivity");
if (!clazz){
__android_log_print(ANDROID_LOG_INFO, "native", "cannot get class: com/example/efan/jni_learn2/MainActivity");
return -1;
}
if(env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])))
{
__android_log_print(ANDROID_LOG_INFO, "native", "register native method failed!\n");
return -1;
}
return JNI_VERSION_1_4;
}
JNINativeMethod
在動態(tài)注冊的過程中使用到了結(jié)構(gòu)體 JNINativeMethod 用于記錄 java 方法與 jni 函數(shù)的對應(yīng)關(guān)系
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
結(jié)構(gòu)體的第一個參數(shù) name 是java 方法名;
第二個參數(shù) signature 用于描述方法的參數(shù)與返回值第焰;
第三個參數(shù) fnPtr 是函數(shù)指針买优,指向 jni 函數(shù);
其中挺举,第二個參數(shù) signature 使用字符串記錄方法的參數(shù)與返回值杀赢,具體格式形如“()V”、“(II)V”湘纵,其中分為兩部分脂崔,括號內(nèi)表示的是參數(shù),括號右側(cè)表示的是返回值梧喷;
數(shù)據(jù)類型映射
- 基本數(shù)據(jù)類型
java 類型 | native 類型 | 域描述符 | 補充 |
---|---|---|---|
boolean | jboolean | Z | |
byte | jbyte | B | |
char | jchar | C | |
short | jshort | S | |
int | jint | I | |
long | jlong | J | |
float | jfloat | F | |
double | jdouble | D | |
void | void | V |
-
數(shù)組引用類型
如果是一維數(shù)組則遵循下表脱篙,如果是二維數(shù)組或更高維數(shù)組則對應(yīng)的 native 類型為 jobjectArray,域描述符中使用 '[' 的個數(shù)表示維數(shù)
java 類型 | native 類型 | 域描述符 | 補充 |
---|---|---|---|
int[] | jintArray | [I | |
float[] | jfloatArray | [f | |
byte[] | jbyteArray | [B | |
char[] | jcharArray | [C | |
short[] | jshortArray | [S | |
double[] | jdoubleArray | [D | |
long[] | jlongArray | [F | |
boolean[] | jbooleanArray | [Z |
- 對象引用類型
對于其它引用類型伤柄,即 java 中的對象,其映射規(guī)則為
java 類型 | native 類型 | 域描述符 | 補充 |
---|---|---|---|
類名(如 Surface) | 通常是 jobject文搂,僅有一種例外适刀,如果 java 類型是 String,則對應(yīng)的native 類型是 jstring | 以"L"開頭煤蹭,以";"結(jié)尾中間是用"/" 隔開的包及類名(如 Landroid/view/Surface;)如果內(nèi)部類則使用$連接內(nèi)部類笔喉; |
-
對象數(shù)組引用類型
如果是一維數(shù)組則遵循下表,如果是二維數(shù)組或更高維數(shù)組則對應(yīng)的 native 類型為 jobjectArray硝皂,域描述符中使用 '[' 的個數(shù)表示維數(shù)
java 類型 | native 類型 | 域描述符 | 補充 |
---|---|---|---|
類名(如 Surface) | 通常是 jobject常挚,僅有一種例外,如果 java 類型是 String稽物,則對應(yīng)的native 類型是 jstring | 在對象引用類型的域描述符的基礎(chǔ)上在左邊添加'['字符 |
jni 函數(shù)默認參數(shù)
在 jni 函數(shù)中有兩個默認參數(shù)
JNIEnv *env, jobject thiz
其中 JNIEnv 指代的是當前 java 環(huán)境奄毡,可以利用 JNIEnv 可以操作 java 層代碼;jobject 指代的是 jni 函數(shù)對應(yīng)的 java native 方法的類實例贝或,如果 java 方法是 static吼过,則代表的是 class 對象;