JNI頭文件分析 為了分析JNI頭文件,特意寫了HeaderTest這個(gè)包含多個(gè)不同的native方法的測(cè)試類,。 江蘇 無錫 繆小東 1. 測(cè)試文件HeaderTest.java 這是一個(gè)相當(dāng)簡單的文件。前兩個(gè)為成員變量,、第三個(gè)為非本地方法,,主要考察成員變量和非本地方法,是否在頭文件中出現(xiàn)。后面一段是幾種類型的本地方法,,例如無輸入無輸出的本地方法,、輸入輸出基本數(shù)據(jù)類型的本地方法、輸入輸出String類型的本地方法,、輸入輸出java自定義的對(duì)象的本地方法,、輸出java自定義的接口的本地方法、輸出自定義的對(duì)象的本地方法,。后面的就是這幾種類型對(duì)應(yīng)的輸入輸出為數(shù)組的方法,。最后總和的方法。在此類的外部有一個(gè)自定義的輔助類Student,。本例中還考察private,、friendly、public等訪問權(quán)限對(duì)本地方法產(chǎn)生頭文件的影響,。以及方法重載對(duì)頭文件中相應(yīng)方法的影響……太羅嗦了,!大家就看看下面的java文件吧!??! import java.util.*; public class HeaderTest{ private int i = 0 ; float f = public void XXX(String s){ ; } private native void doVoid(); native int doShort(); native void doArray(Object[] o ); native int doInt(int i); //byte ,short ,int,long,float,double ,boolean,char native int doInt(double d); //byte ,short ,int,long,float,double ,boolean,char native int doInt(Object o); native int doInt(double d1,double d2); static native int doInt(double d1 ,double d2,double d3); static native int doInt(double d1 ,float f,boolean b ,char[] c ); native int doInt(int[] i); native int doInt(int[] i1,double[] i2 ); static native int doInt(int[] i1,double[] i2 ,Object[] o ); public native String doString(String s); public native Object doObject(Object o ); public native Enumeration doInterface(Iterator it); public native Student doStudent(Student s); // native int[] doInt(int[] i); //byte ,short ,int,long,float,double ,boolean,char public native String[] doString(String[] s); public native Object[] doObjects(Object[] o ); public native Enumeration[] doInterface(Iterator[] it); public native Student[] doStudent(Student[] s); public native static Object doAll(int[] i , String[] s , Student[] student ); } class Student{} 二、頭文件HeaderTest.h 編譯上面的文件,,然后對(duì)編譯后的類文件,,使用javah命令,產(chǎn)生HeaderTest.h頭文件,。下面就是該頭文件的一部分,。 1. 2. #include <jni.h> 3. 4. #ifndef _Included_HeaderTest 5. #define _Included_HeaderTest 6. #ifdef __cplusplus 7. extern "C" { 8. #endif 9. 14. JNIEXPORT void JNICALL Java_HeaderTest_doVoid (JNIEnv *, jobject); …… 1. #ifdef __cplusplus 2. } 3. #endif 4. #endif 為了閱讀的方便,給此頭文件加了行號(hào),! 三,、JNI頭文件的組成 JNI頭文件一般由三部分組成:1.頭文件;2.本地方法的注冊(cè),;3.本地方法在頭文件中的表示,。 1. 1-8 是C、C++的頭,,它包含了我們?cè)?/span>VC工具中要使用的jni.h的頭文件,,以及其它的一些指令。(相信你對(duì)C,、C++比較了解吧,!不了解可以閱讀相關(guān)書籍!我這里就不講解了) 2. 以后的每隔6行就是一個(gè)本地方法對(duì)應(yīng)的頭文件中的方法,。這些方法的注釋部分包含Class,、Method,、和Signature三部分。Class當(dāng)然就是具有此本地方法的類的名稱了,,在我們的例子中為HeaderTest,;Method就是本地方法的名稱了,我們例子中有如doStudent的方法,;Signature是方法的標(biāo)識(shí),,它是一個(gè)標(biāo)識(shí)符,主要供我們?cè)?/span>JNI操作java對(duì)象的方法使用的,。本節(jié)只簡單講述其特征,,具體使用見本博客的后續(xù)文章。 3. 接著就是一個(gè)本地方法在頭文件中的表示了,!例如: JNIEXPORT jobject JNICALL Java_HeaderTest_doObject (JNIEnv *, jobject, jobject); 四,、分析 1.方法組成分析 從上面的頭文件可以看出:頭文件包含以下7個(gè)部分: JNIEXPORT <JNIType> JNICALL Java_HeaderTest_doVoid (JNIEnv *, jobject); 1 2 3 4 5 6 7 我就簡單地介紹這7個(gè)部分吧! 1) JNIEXPORT(1)和JNICALL(3)都是JNI的關(guān)鍵字,,表示此函數(shù)是要被JNI調(diào)用的,。 2) <JNIType>(2)為本地方法返回的數(shù)據(jù)類型,對(duì)應(yīng)與java源文件中的該方法返回的數(shù)據(jù)類型 3) Java(4)為JNI中標(biāo)識(shí)此方法來源于java的標(biāo)識(shí)頭 4) _HeaderTest(5)表示該方法所在的類,。當(dāng)該類在包中時(shí)前面會(huì)按次序加上包名,。我將上面的類文件放入名為mxd的包中,相應(yīng)的5部分就為mxd_HeaderTest了,,其它類推吧,! 5) _doVoid(6)為JNI中對(duì)應(yīng)與java文件中本地方法的方法名。這不是完全對(duì)應(yīng)的哦,!重載方法是一個(gè)例外! 6) (JNIEnv *, jobject)(7)方法的最后一部分哦,!比較復(fù)雜,,它們有一個(gè)共同的特點(diǎn),包含JNIEnv *――它是一個(gè)接口指針,,用于定位函數(shù)表中的函數(shù),!在JNI規(guī)范中一般稱為“Interface Pointer”??吹竭@兒好像和過程調(diào)用很類似了,!是的,JNI中的操作過程,,就是面向過程的,!后面的jobject是一個(gè)指向該類的指針,類似與C語言中的this,。這個(gè)第二個(gè)參數(shù)是變化的,,當(dāng)該方法為類的實(shí)例方法時(shí)該參數(shù)為jobject,;當(dāng)該方法為類方法(即靜態(tài)方法)時(shí)該參數(shù)為jclass,指向該類的class,。詳細(xì)見下面,。 2.java的數(shù)據(jù)類型和JNI中數(shù)據(jù)類型的映射 以下是HeaderTest.java的源文件中的本地方法和HeaderTest.h頭文件中的方法對(duì)照! private native void doVoid(); native int doInt(int i); //byte ,short ,int,long,float,double ,boolean,char public native String doString(String s); public native Object doObject(Object o ); public native Enumeration doInterface(Iterator it); public native Student doStudent(Student s); native int[] doInt(int[] i); //byte ,short ,int,long,float,double ,boolean,char public native String[] doString(String[] s); public native Object[] doObjects(Object[] o ); public native Enumeration[] doInterface(Iterator[] it); public native Student[] doStudent(Student[] s); JNIEXPORT void JNICALL Java_HeaderTest_doVoid (JNIEnv *, jobject); JNIEXPORT jint JNICALL Java_HeaderTest_doInt__I (JNIEnv *, jobject, jint); JNIEXPORT jstring JNICALL Java_HeaderTest_doString__Ljava_lang_String_2 (JNIEnv *, jobject, jstring); JNIEXPORT jobject JNICALL Java_HeaderTest_doObject (JNIEnv *, jobject, jobject); JNIEXPORT jobject JNICALL Java_HeaderTest_doInterface__Ljava_util_Iterator_2 (JNIEnv *, jobject, jobject); JNIEXPORT jobject JNICALL Java_HeaderTest_doStudent__LStudent_2 (JNIEnv *, jobject, jobject); JNIEXPORT jintArray JNICALL Java_HeaderTest_doInt___3I (JNIEnv *, jobject, jintArray); JNIEXPORT jobjectArray JNICALL Java_HeaderTest_doString___3Ljava_lang_String_2 (JNIEnv *, jobject, jobjectArray); JNIEXPORT jobjectArray JNICALL Java_HeaderTest_doObjects (JNIEnv *, jobject, jobjectArray); JNIEXPORT jobjectArray JNICALL Java_HeaderTest_doInterface___3Ljava_util_Iterator_2(JNIEnv *, jobject, jobjectArray); JNIEXPORT jobjectArray JNICALL Java_HeaderTest_doStudent___3LStudent_2 (JNIEnv *, jobject, jobjectArray); JNIEXPORT jobject JNICALL Java_HeaderTest_doAll (JNIEnv *, jclass, jintArray, jobjectArray, jobjectArray); 從上面的對(duì)比可以看到: 1) java中的返回值void和JNI中的void是完全對(duì)應(yīng)的哦?。▋H僅一個(gè)而已),。 2) java中的基本數(shù)據(jù)類型(byte ,short ,int,long,float,double ,boolean,char-8種)在JNI中對(duì)應(yīng)的數(shù)據(jù)類型只要在前面加上j就對(duì)應(yīng)了(jbyte ,jshort ,jint,jlong,jfloat,jdouble ,jboolean,jchar)。 3) java中的對(duì)象,,包括類庫中定義的類,、接口以及自定義的類接口,都對(duì)應(yīng)于JNI中的jobject,。 4) java中基本數(shù)據(jù)類型的數(shù)組對(duì)應(yīng)與JNI中的j<type>array類型,。(type就是上面說的8種基本數(shù)據(jù)類型) 5) java中對(duì)象的數(shù)組對(duì)應(yīng)于JNI中的jobjectArray類型。(在java中一切對(duì)象,、接口以及數(shù)組都是對(duì)象) 下圖是JNI規(guī)范中java數(shù)據(jù)類型和JNI數(shù)據(jù)類型的映射圖,。 第一幅為基本數(shù)據(jù)類型的映射圖: 第二幅為引用數(shù)據(jù)類型的映射圖: 3.重載方法的區(qū)別 以下是幾個(gè)沒有重載的方法及其在JNI頭文件中對(duì)應(yīng)的方法: private native void doVoid(); native int doShort(); native void doArray(Object[] o ); JNIEXPORT void JNICALL Java_HeaderTest_doVoid (JNIEnv *, jobject); JNIEXPORT jint JNICALL Java_HeaderTest_doShort (JNIEnv *, jobject); JNIEXPORT void JNICALL Java_HeaderTest_doArray (JNIEnv *, jobject, jobjectArray); 從上面的例子可以看出:沒有重載的方法在JNI的頭文件中對(duì)應(yīng)的方法和java中本地方法的名稱是一致的。 以下是幾個(gè)簡單的重載方法: native int doInt(int i); //byte ,short ,int,long,float,double ,boolean,char native int doInt(double d); //byte ,short ,int,long,float,double ,boolean,char native int doInt(double d1,double d2); static native int doInt(double d1 ,double d2,double d3); static native int doInt(double d1 ,float f,boolean b ,char[] c ); native int doInt(Object o); JNIEXPORT jint JNICALL Java_HeaderTest_doInt__I (JNIEnv *, jobject, jint); JNIEXPORT jint JNICALL Java_HeaderTest_doInt__D (JNIEnv *, jobject, jdouble); JNIEXPORT jint JNICALL Java_HeaderTest_doInt__DD(JNIEnv *, jobject, jdouble, jdouble); JNIEXPORT jint JNICALL Java_HeaderTest_doInt__DDD (JNIEnv *, jclass, jdouble, jdouble, jdouble); JNIEXPORT jint JNICALL Java_HeaderTest_doInt__DFZ_ JNIEXPORT jint JNICALL Java_HeaderTest_doInt__Ljava_lang_Object_2 (JNIEnv *, jobject, jobject); 從上面的對(duì)比可以看出:重載方法時(shí),,方法的后面還有一個(gè)表示方法參數(shù)的“后綴”,。例如上面操作的變量為Int時(shí)加一個(gè)表示此類型的標(biāo)識(shí)“I”;多個(gè)類型當(dāng)然依次加多個(gè)了,;當(dāng)某個(gè)變量為數(shù)組時(shí)在其類型前面會(huì)加一個(gè)“ 下面是復(fù)雜的例子: public native Student[] doStudent(Student[] s1); public native Student[] doStudent(Student[] s1,Student[] s2); JNIEXPORT jobjectArray JNICALL Java_HeaderTest_doStudent___3LStudent_2 (JNIEnv *, jobject, jobjectArray); JNIEXPORT
jobjectArray JNICALL
Java_HeaderTest_doStudent___3LStudent_2_3LStudent_2(JNIEnv *, jobject,
jobjectArray, jobjectArray); 從上面的例子可以看出,,方法重載時(shí),方法后要依次加上參數(shù)名稱,,前面知道了數(shù)組要在參數(shù)類型前加“ 4.實(shí)例方法和靜態(tài)方法的區(qū)別 以下是本地實(shí)例方法和本地類方法的比較,。紅色的為java中的本地方法,藍(lán)色的為JNI頭文件中對(duì)應(yīng)的方法,。 public native Student[] doStudent(Student[] s); public native static Object doAll(int[] i , String[] s , Student[] student ); JNIEXPORT jobjectArray JNICALL Java_HeaderTest_doStudent___3LStudent_2 (JNIEnv *, jobject, jobjectArray); JNIEXPORT jobject JNICALL Java_HeaderTest_doAll (JNIEnv *, jclass, jintArray, jobjectArray, jobjectArray); 從上面的比較可以看出: 在本地實(shí)例方法中該參數(shù)為jobject,,代表對(duì)象本身,在本地靜態(tài)方法中該參數(shù)為jclass,。這是它們的區(qū)別,。關(guān)于如何操作代表該實(shí)例的jobject和操作該類的jclass請(qǐng)關(guān)注后續(xù)文章,。 5.Signature 在JNI頭文件中提到Signature,它主要用于操作Java類中的方法,。Signature一般由兩部分組成:1.方法參數(shù),;2.方法返回值。 1. 方法參數(shù)包含在一個(gè)括號(hào)中,,返回值在括號(hào)外,! 2. 方法參數(shù)個(gè)數(shù)較多時(shí)會(huì)依次以“;”隔開。 3. 當(dāng)參數(shù)或者返回值是基本數(shù)據(jù)類型時(shí),,必須用其在JNI中的描述符表示,。下表就是Java基本數(shù)據(jù)類型對(duì)應(yīng)的JNI中的描述符。 4. 方法參數(shù)或者返回值為java中的對(duì)象時(shí),,必須以“L”加上其路徑,,不過此路徑必須以“/”分開,自定義的對(duì)象也使用本規(guī)則,,不在包中時(shí)直接“L”加上類名稱,。 5. 當(dāng)參數(shù)或者返回值為數(shù)組時(shí),前面必須加上“[”,! 以上就是Signature表示方法的規(guī)則哦,!看看下面一些Signature能一個(gè)個(gè)轉(zhuǎn)換為相應(yīng)的方法嗎? ([LStudent;)[LStudent; ([I[Ljava/lang/String;[LStudent;)Ljava/lang/Object; ([LStudent;[LStudent;)[LStudent; ([Ljava/util/Iterator;)[Ljava/util/Enumeration; ([Ljava/lang/Object;)[Ljava/lang/Object; ([Ljava/lang/String;)[Ljava/lang/String; (LStudent;)LStudent; 能明白就好了,!后續(xù)的使用JNI操作Java對(duì)象你可以入門了哦?。?/span> 6.其它 1. 類中的成員變量,、非本地方法在JNI中是不會(huì)出現(xiàn)的,。即javah產(chǎn)生頭文件時(shí)只會(huì)關(guān)心本地方法。 2. private,、friendly,、protected以及public這些方法限制符不會(huì)在JNI的頭文件中出現(xiàn)。這些訪問修飾符只有在其它類使用這些方法時(shí)有效,!JNI中不關(guān)心此修飾符! 3. 當(dāng)我們給HeaderTest.java指定一個(gè)包時(shí),,不僅頭文件中的方法的前面加了包名,,并且產(chǎn)生的頭文件也變?yōu)?/span>mxd_HeaderTest.h。 |
|