JNI:本地代碼調(diào)用Java代碼
1. 本地代碼訪問(wèn)Java代碼
在被調(diào)用的C/C++函數(shù)中也可以反過(guò)來(lái)訪問(wèn)Java程序中的方法
javah 工具生成的C/C++函數(shù)聲明中,可以看到有兩個(gè)參數(shù):
JNIEXPORT void JNICALL Java_com_omg_NativeLib_sayHello (JNIEnv *env, jobject jobj){cout << "hello world" << endl;}
2. JNIEnv 類型
JNIENV類型實(shí)際上代表了Java環(huán)境,通過(guò)這個(gè)JNIEnv*指針,就可以對(duì)Java端的代碼進(jìn)行操作。例如,創(chuàng)建Java類的對(duì)象、調(diào)用Java對(duì)象的方法、獲取Java對(duì)象的屬性等。JNIEnv的指針會(huì)被JNI傳入到本地方法的實(shí)現(xiàn)函數(shù)中來(lái)對(duì)Java端的代碼進(jìn)行操作。
JNIEnv類中有很多函數(shù)可以使用
NewObject / NewString / NewArray GetField / Set Field GetStaticField / SetStatic Field CallMethod / CallStatic Method
等許許多多的函數(shù)
jobject 什么的一個(gè)類型呢?
不是靜態(tài)方式時(shí),傳入的是當(dāng)前關(guān)聯(lián)的類對(duì)象
靜態(tài)方法時(shí),傳入當(dāng)前類的Class對(duì)象
3. Java的類型在C/C++中的映射關(guān)系
| Java類型 | 本地類型 | JNI定義的別名 |
|---|---|---|
| int | long | jint / jsize |
| long | __int64 | jlong |
| byte | signed char | jbyte |
| boolean | unsigned char | jboolean |
| char | unsigned short | jchar |
| short | short | jshort |
| float | float | jfloat |
| double | double | jdouble |
| Object | _jobject* | jobject |
4. jclass如何獲取
為了能夠在C/C++中使用Java類。JNI.h頭文件中,專門(mén)定義了jclass類型來(lái)表示Java中的Class類
JNIEnv類中有以下幾個(gè)函數(shù)可以獲取jclass:
jclass FindClass(const char* clsName);jclass GetObjectClass(jobject ojb);jclass GetSuperClass(jclass obj);
在Java JDK中也有這幾個(gè)方法
FindClass 將會(huì)在classpath環(huán)境變量下尋找類。傳入完整類名,注意包與包之間是使用 ?“/ “ ,而不是使用 “.“ 來(lái)分隔
jclass cls_str = env->FindClass("java/lang/String");5. 訪問(wèn)Java類中的屬性與方法
在C/C++本地代碼中訪問(wèn)Java端的代碼,一個(gè)常見(jiàn)的場(chǎng)景就是獲取類的屬性和調(diào)用類的方法。為了在C/C++中表示屬性和方法,JNI在jni.h頭文件中定義了jfieldID和jmethdID類型來(lái)分別代表Java端的屬性和方法
在訪問(wèn),或者設(shè)置Java屬性的時(shí)候,首先就要先在本地代碼中獲得代表Java屬性的jfieldID;然后才能在本地代碼中進(jìn)行Java的屬性操作。同樣的,在需要調(diào)用Java的方法時(shí),也是需要先獲取到代表該方法的jmethodID才能進(jìn)行Java方法調(diào)用
使用JNIEnv的
GetFieldID / GetMethodIDGetStaticFieldID / GetStaticMethodID
來(lái)取得相應(yīng)的jfieldID和jmethodID
來(lái)看下方法定義
jfieldID GetFieldID(jclass clazz, const char *name, const char *sig);jmethodID GetMethodID(jclass clazz, const char *name, const char *sig);jfieldID GetStaticFieldID(jclass clazz, const char *name, const char *sig);jmethodID GetStaticMethodID(jclass clazz, const char *name, const char *sig);
GetMethodID也能夠獲得構(gòu)造函數(shù)的jmethodID。創(chuàng)建一個(gè)Java對(duì)象時(shí)可以調(diào)用指定的構(gòu)造方法,這個(gè)將在后面向大家介紹
如:env -> GetMethodID(data_Clazz, "
類似Java的發(fā)射(Reflect),需要指定類跟屬性/方法名來(lái)獲取相應(yīng)的jfieldID或者jmethodID。而sign又是什么呢?
6. Sign是什么?
例如:TestNative類中有兩個(gè)重載的方法
package com.omg;public class TestNative {public void function( int i ){System.out.println("Integer: " + i);}public void function( double d ){System.out.println("double: " + d);}}
然后再C/C++代碼中調(diào)用其中的一個(gè)function方法的話;
//首先取得要調(diào)用的方法所在的類jclass clazz_TestNative = env->FindClass("com/omg/TestNative");//獲取jmethodID之后才能進(jìn)行調(diào)用jmethodID id_func = env->GetMethodID(clazz_TestNative, "function", ??);
但是到底取得的是 void function(int i) 方法還是 void function(double d) 方法的jmethodID呢?
這就是sign的作用了,他用于指定要取得的屬性/方法的類型。
這里的sign如果指定為"(I)V",則獲得的是 void function(int i) 方法的jmethodID;如果指定為"(D)V",則獲得的是 void function(double d) 方法的jmethodID
7. sign簽名
用來(lái)表示要取得的屬性/方法的類型
| 類型 | 簽名 |
|---|---|
| boolean | Z |
| byte | B |
| char | C |
| short | S |
| int | I |
| long | L |
| float | F |
| double | D |
| void | V |
| object | L + 用/分隔包的完整類名 + ; |
| Array | [ + 簽名 ? 例如,[I、[Ljava/lang/String; |
| Method | (參數(shù)1類型簽名 參數(shù)2類型簽名...)返回值類型簽名 |
8. 使用簽名獲得屬性/方法ID的例子(復(fù)雜)
import java.util.Date;public class HelloNative(){public int property;public int function(int foo, Date date, int[] arr){System.out.println("function");}public native void test();}
test本地方法的實(shí)現(xiàn)
JNIEXPORT void Java_Hello_test(JNIEnv* env, jboject ojb) {//因?yàn)閠est不是靜態(tài)函數(shù),所以傳進(jìn)來(lái)的就是調(diào)用這個(gè)函數(shù)的對(duì)象jclass hello_clazz = env->GetObjectClass(obj);jfieldID filedID_prop = env->GetFieldID(hello_clazz, "property", "I");jmethodID methodID_func = env->GetMethodID("hello_clazz", "function", "(ILjava/util/Date;[I)I");env->CallIntMethod(obj, methodID_func, 0L, NULL, NULL); //invoke}
取得的property是int類型的,所以在簽名中傳入"I"
取得function的ID時(shí)

所以在最后得到(ILjava/util/Date;[I)I
9. 使用javap命令來(lái)產(chǎn)生簽名
JDK提供了一個(gè)工具來(lái)查看一個(gè)類的申請(qǐng),其中就可以設(shè)置輸出每一個(gè)方法/屬性的簽名
java -s -p [full Calss Name]
-s 表示輸出簽名信息
-p 通-private,輸出包括private訪問(wèn)權(quán)限的成員信息

分享&在看
