四 JNI中的Java對象
Java本機接口提供了一個函數集來處理Java對象(使用方法/域)、句柄異常和用於線程的數據同步。這些函數在本機提供更好的訪問Java對象的能力,允許用於更復雜的應用程序。這些函數的用法之一是被用來產生一個Java方法的回調,或者產生通信信息的回調。
1.訪問JNI中的域
在Java類中有兩種成員變量類型:靜態域,它們屬於類;非靜態域,它們屬於單個對象。爲了可以訪問一個域,必須向GetFieldID或GetStatiieldID傳遞一個域描述符以及域的名稱。域描述符是完整描述域類型的一個或多個字符。例如,域int的域描述符是I。用於數組的域描述符是以字符 [ 爲前綴,用於數組的每一維。例如,int[]的域描述符是[I,int[][]的域描述符是[[I。對於引用類型,使用了類的完整限制符名稱,但是點號被正斜槓替換,描述符將在開始被一個L,在結尾被一個分號所包圍。例如,類型java.lang.Integer的域描述符是Ljava/lang/Integer。基本類型域描述符如下表:
基本類型 域描述符
boolean Z
byte B
char C
short S
int I
long J
float F
double D
2.訪問域的函數
(1).下面的函數向指定域返回一個句柄,以便用於Get以及Set函數。GetObjectClass函數可以用來獲取一個適合於該函數第一個參數的jclass。name是域的名稱,sig是域描述符。如果函數失敗,返回NULL:
jfieldID GetFieldID(jclass clazz, constchar *name, constchar *sig);
(2).下面的函數返回屬於Java對象obj的,被fieldID指定的一個特定域:
[NativeType] Get[Type]Field(jobject obj, jfieldID fieldID);
(3).SetField函數將屬於Java對象obj的,被fieldID指定的一個特定域的值設置爲val:
void Set[Type]Field(jobject obj,jfieldID fieldID, [NativeType] val);
(4)GetStatiieldID函數同GetFieldID函數功能相同,但它用於獲取一個靜態域的句柄:
jfieldID GetStatiieldID(jclass clazz,constchar *name, constchar *sig);
(5)GetStatiield函數返回屬於clazz描述的類,fieldID指定的一個靜態域的值:
[NativeType] GetStatic[Type]Field(jclass clazz,jfieldID fieldID);
(6).SetStatiield函數設置屬於clazz描述的類,fieldID指定一個靜態域的值:
void SetStatic[Type]Field(jclass clazz, jfieldID fieldID, [NativeType] value);
3.使用JNI調用Java方法
使用JNI可以調用Java中的靜態及非靜態方法,這時需要使用方法的名稱和方法描述符來獲得特定Java方法的句柄,然後才能通過CallMethod函數調用該Java方法。
方法描述符通過將所有方法的參數類型放置到一個單一圓括號集中,然後在圓括號之後指定返回類型而形成。用於參數的類型以及返回類型使用在前面描述過的域描述符。如果方法返回void,則描述符就是V。若方法沒有任何參數,則圓括號中爲空。用於熟悉的main方法的方法描述符是([Ljava/lang/String;)V。如果希望調用構造函數,使用方法名<init>,對於靜態構造函數,則使用<clinit>。
JNI調用Java方法的步驟:
獲取方法和變量描述符的編譯命令:javap -s -classpath . [PacketName+"."+Library Name] 如:javap -s -classpath . com.example.MyProject.MyLibrary
(1).獲取要調用方法所在的類:
jclass GetObjectClass(jobject obj);
(2).通過jclass獲取要調用的方法的methodID,即方法句柄:
/**
*使用步驟(1)獲取的jclass
*方法名稱
*方法描述符
*/
jmethod GetMethodID(jclass clazz,constchar *name,constchar *sig);
jmethod GetStaticMethodID(jclass clazz,constchar* name,constchar *sig);
(3).通過方法句柄調用方法,注意:如果希望調用一個構造函數或一個私有方法,方法ID將會基於實際對象的類獲得,而不是基於對象的一個超類。
/**
*java環境傳入的jobject
*方法句柄
*/參數:接受大量參數,並將這些參數直接傳送到Java方法中
[NativeType] Call[Type]Method(jobject obj, jmethodID methodID,…);
/**
*該方法將參數列表作爲一個va_list結構接受,該結構是隨同參數列
*表預包裝的。
*/
[NativeType] Call[Type]MethodV(jobject obj,jmethodID methodID,va_list args);
/**
*該方法將參數作爲一個jvalue數組接受,它是可以使用任意本機數據*類型版本的Java數據類型形式的一個聯合(union),包括jobject。
*/
[NativeType] Call[Type]MethodA(jobject obj, jmethodID methodID, const jvalue *args);
/**
*下面三個方法也調用對象方法的一個實例,但是調用的Java方法是
*基於jclass數的。這些函數可以在對象的層次結構中調用一個指定的方法,而不是僅僅基於對象的類調用一個方法。
*jclass應通過傳入要調用的方法所在的類的實例來獲得。
*/
[NativeType] CallNonvirtual[Type]Method(jobject obj, jclass clazz, jmethodID methodID, …);
[NativeType] CallNonvirtual[Type]MethodV(jobject obj, jclass clazz, jmethodID methodID, va_list args);
[NativeType] CallNonvirtual[Type]MethodA(jobject obj, jclass clazz, jmethodID methodID, const jvalue *args);
/**
*下面的函數調用屬於傳入的clazz類的一個靜態方法。使用Get
*StaticMethodID來獲得方法句柄。
*/
[NativeType] CallStatic[Type]Method(jclass clazz, jmethodID methodID, …);
[NativeType] CallStatic[Type]MethodV(jclass clazz, jmethodID methodID, va_list args);
[NativeType] CallStatic[Type]MethodA(jclass clazz, jmethodID methodID, const jvalue *args);