技術轉載:Jni學習二:JNI 數據類型

轉自:http://wxiaolei.blog.163.com/blog/static/10387601200841762812328/

通過本章可以瞭解c文件如何使用java複雜的數據類型。

 

 

從Java 1.1開始,Java Native Interface (JNI)標準成爲java平臺的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI一開始是爲了本地已編譯語言,尤其是C和C++而設計 的,但是它並不妨礙你使用其他語言,只要調用約定受支持就可以了。讓我們看一些使用JNI的簡單例子吧。

使用java與本地已編譯的代碼交互,通常會喪失平臺可移植性。但是,有些情況下這樣做是可以接受的,甚至是必須的,比如,使用一些舊的庫,與硬件、操作系統進行交互,或者爲了提高程序的性能。JNI標準至少保證本地代碼能工作在任何Java 虛擬機實現下。

開始:

如果你習慣了使用JNI,你就不會覺得它難了。既然本地方法是由其他語言實現的,它們在Java中沒有函數體。但是,所有本地代碼必須用本地關鍵詞 聲明,成爲Java類的成員。清單A演示了一個簡單的類,它申明瞭一個本地的(native),靜態的(static)方法:sum。

寫完了你的Java類,接下來就要寫本地代碼。本地方法符號提供一個滿足約定的頭文件,使用Java工具可以很容易地創建它而不用手動去創建。你對 Java的class文件使用javah命令,就會爲你生成一個對應的C/C++頭文件。清單B就是爲清單A的Test1類創建的頭文件。注意:它創建了 一個C/C++函數:Java_Test1_sum。

執行本地方法:

一旦你有了這個頭文件,你就需要寫頭文件對應的本地方法,就像我在清單C做的那樣。注意:所有的本地方法的第一個參數都是指向JNIEnv結構的。 這個結構是用來調用JNI函數的,(我會在另一個章節中討論)。第二個參數jclass的意義,要看方法是不是靜態的(static)或者實例 (Instance)的。前者,jclass代表一個類對象的引用,而後者是被調用的方法所屬對象的引用。最後的兩個jint參數表示了Java方法的 int參數。

返回值和參數類型根據等價約定映射到本地C/C++類型,如表A所示。有些類型,如清單B裏面的兩個jint參數,在本地代碼中可直接使用,而其他類型只有通過JNI調用操作。

表A

Java類型 本地類型 描述
boolean jboolean C/C++8位整型
byte jbyte C/C++帶符號的8位整型
char jchar C/C++無符號的16位整型
short jshort C/C++帶符號的16位整型
int jint C/C++帶符號的32位整型
long jlong C/C++帶符號的64位整型e
float jfloat C/C++32位浮點型
double jdouble C/C++64位浮點型
Object jobject 任何Java對象,或者沒有對應java類型的對象
Class jclass Class對象
String jstring 字符串對象
Object[] jobjectArray 任何對象的數組
boolean[] jbooleanArray 布爾型數組
byte[] jbyteArray 比特型數組
char[] jcharArray 字符型數組
short[] jshortArray 短整型數組
int[] jintArray 整型數組
long[] jlongArray 長整型數組
float[] jfloatArray 浮點型數組
double[] jdoubleArray 雙浮點型數組

※     JNI類型映射

最後一步是把本地代碼編譯成共享庫(比如,UNIX的so文件,Windows的dll文件)。在Java中調用方法前,共享庫須通過System.loadLibrary導入。最常用的方式是在類的靜態(static)初始化器裏做這這個工作。


 

在本地代碼中訪問JNI

我舉的例子很簡單,並不能滿足演示怎樣寫JNI方法的目標。現在,讓我們看一些高級的,通過JNIEnv結構使用非簡單類型的例子。

JNI通過函數的形式提供了很多功能,供本地代碼通過指向JNIEnv結構的指針調用;它作爲第一個參數傳遞給每個本地方法。JNI函數的調用有下面幾種格式(這裏,假設env是指向JNIEnv的指針):

//C 格式

(*env)-><jni function>( env, <parameters> )

//C++ 格式

env-><jni function>( < parameters> )

這篇文章中接下來的例子我將會用C++格式。

使用數組:

JNI通過JNIEnv提供的操作Java數組的功能。它提供了兩個函數:一個是操作java的簡單型數組的,另一個是操作對象類型數組的。

因爲速度的原因,簡單類型的數組作爲指向本地類型的指針暴露給本地代碼。因此,它們能作爲常規的數組存取。這個指針是指向實際的Java數組或者Java數組的拷貝的指針。另外,數組的佈置保證匹配本地類型。

爲了存取Java簡單類型的數組,你就要要使用GetXXXArrayElements函數(見表B),XXX代表了數組的類型。這個函數把Java數組看成參數,返回一個指向對應的本地類型的數組的指針。

表B

函數 Java數組類型 本地類型
GetBooleanArrayElements jbooleanArray jboolean
GetByteArrayElements jbyteArray jbyte
GetCharArrayElements jcharArray jchar
GetShortArrayElements jshortArray jshort
GetIntArrayElements jintArray jint
GetLongArrayElements jlongArray jlong
GetFloatArrayElements jfloatArray jfloat
GetDoubleArrayElements jdoubleArray jdouble

JNI數組存取函數

當你對數組的存取完成後,要確保調用相應的ReleaseXXXArrayElements函數,參數是對應Java數組和 GetXXXArrayElements返回的指針。如果必要的話,這個釋放函數會複製你做的任何變化(這樣它們就反射到java數組),然後釋放所有相 關的資源。

爲了使用java對象的數組,你必須使用GetObjectArrayElement函數和SetObjectArrayElement函數,分別去get,set數組的元素。GetArrayLength函數會返回數組的長度。

清單D包含了一個簡單的類,它演示了本地代碼如何使用Java數組。這個本地實現循環遍歷一個整型(int)數組,返回這些元素的總和。爲簡單起見,這個清單包含了java代碼和本地實現。我已經省略了頭文件,它可以很方便地通過javah得到。


 

在本地代碼中訪問JNI

使用對象

詳細請參照:Jni學習三:jni使用對象詳解

JNI提供的另外一個功能是在本地代碼中使用Java對象。通過使用合適的JNI函數,你可以創建Java對象,get、set 靜態(static)和實例(instance)的域,調用靜態(static)和實例(instance)函數。JNI通過ID識別域和方法,一個域或 方法的ID是任何處理域和方法的函數的必須參數。

表C列出了用以得到靜態(static)和實例(instance)的域與方法的JNI函數。每個函數接受(作爲參數)域或方法的類,它們的名稱,符號和它們對應返回的jfieldID或jmethodID。

表C

函數 描述
GetFieldID 得到一個實例的域的ID
GetStaticFieldID 得到一個靜態的域的ID
GetMethodID 得到一個實例的方法的ID
GetStaticMethodID 得到一個靜態方法的ID

※域和方法的函數

如果你有了一個類的實例,它就可以通過方法GetObjectClass得到,或者如果你沒有這個類的實例,可以通過FindClass得到。符號是從域的類型或者方法的參數,返回值得到字符串,如表D所示。

 

表D

Java 類型 符號
boolean Z
byte B
char C
short S
int I
long L
float F
double D
void V
objects對象 Lfully-qualified-class-name;L類名
Arrays數組 [array-type [數組類型
methods方法 (argument-types)return-type(參數類型)返回類型

※確定域和方法的符號

一旦你有了類和方法或者域的ID,你就能把它保存下來以後使用,而沒有必要重複去獲取。

有幾個分別訪問域和方法的函數。實例的域可以使用對應域的GetXXXField的變體函數訪問。GetStaticXXXField函數用於靜態類型。設置域的值,用SetXXXField 和SetStaticXXXField函數。表E包含了所有訪問域的函數列表。

表E

Java 類型 Method方法
boolean GetBooleanField, GetStaticBooleanField, SetBooleanField,SetStaticBooleanField
byte GetByteField, GetStaticByteField, SetByteField, SetStaticByteField
char GetCharField, GetStaticCharField, SetCharField, SetStaticCharField
short GetShortField, GetStaticShortField, SetShortField, SetStaticShortField
int GetIntField, GetStaticIntField, SetIntField, SetStaticIntField
long GetLongField, GetStaticLongField, SetLongField, SetStaticLongField
float GetFloatField, GetStaticFloatField, SetFloatField, SetStaticFloatField
double GetDoubleField, GetStaticDoubleField, SetDoubleField, SetStaticDoubleField
object GetObjectField, GetStaticObjectField, SetObjectField, SetStaticObjectField

※訪問域的函數

另外,方法的訪問是由CallXXXMethod 函數和CallStaticXXXMethod函數完成的,XXX表明了方法的返回值類型。這些函數的變體允許傳遞數組參數 (CallXXXMethodA and CallStaticXXXMethodA)或者傳遞一個可變大小的列表(CallXXXMethodV and CallStaticXXXMethodV)。


 

一個完整的列表

表F:一個完整的列表

返回類型 函數
boolean CallBooleanMethod, CallBooleanMethodA, CallBooleanMethodV, CallStaticBooleanMethod, CallStaticBooleanMethodA, CallStaticBooleanMethodV
byte CallByteMethod, CallByteMethodA, CallByteMethodV, CallStaticByteMethod, CallStaticByteMethodA, CallStaticByteMethodV
char CallCharMethod, CallCharMethodA, CallCharMethodV, CallStaticCharMethod, CallStaticCharMethodA, CallStaticCharMethodV
short CallShortMethod, CallShortMethodA, CallShortMethodV, CallStaticShortMethod, CallStaticShortMethodA, CallStaticShortMethodV
int CallIntMethod, CallIntMethodA, CallIntMethodV, CallStaticIntMethod, CallStaticIntMethodA, CallStaticIntMethodV
long CallLongMethod, CallLongMethodA, CallLongMethodV, CallStaticLongMethod, CallStaticLongMethodA, CallStaticLongMethodV
float CallFloatMethod, CallFloatMethodA, CallFloatMethodV, CallStaticFloatMethod, CallStaticFloatMethodA, CallStaticFloatMethodV
double CallDoubleMethod, CallDoubleMethodA, CallDoubleMethodV, CallStaticDoubleMethod, CallStaticDoubleMethodA, CallStaticDoubleMethodV
void CallVoidMethod, CallVoidMethodA, CallVoidMethodV, CallStaticVoidMethod, CallStaticVoidMethodA, CallStaticVoidMethodV
object CallObjectMethod, CallObjectMethodA, CallObjectMethodV, CallStaticObjectMethod, CallStaticObjectMethodA, CallStaticObjectMethodV

※方法訪問函數

清單E演示瞭如何在本地代碼中調用方法。本地方法printRandom得到了靜態方法Math.random的ID,並且調用它幾次,打印出結果。實例方法也一樣處理。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章