一、 JNI概述
JavaNative Interface的縮寫(xiě),中文為Java本地調用。從Java1.1開(kāi)始,JNI即成為Java標準的一部分。
JNI設計的目的是為了允許Java代碼與其他語(yǔ)言進(jìn)行交互。但這樣做通常會(huì )導致喪失平臺可移植性,通常是在特定的需求下進(jìn)行,例如使用舊的其他語(yǔ)言的庫、需要獲得Java類(lèi)庫不支持的某種基于具體平臺的特性、大量數學(xué)計算性能優(yōu)化等。
二、 JNI數據類(lèi)型和數據結構
1.基本類(lèi)型
JNI基本類(lèi)型和本地等效類(lèi)型的對應表格如下:
| Java類(lèi)型 | 本地類(lèi)型 | 說(shuō)明 |
| boolean | jboolean | 無(wú)符號,8位 |
| byte | jbyte | 無(wú)符號,8位 |
| char | jchar | 無(wú)符號,16位 |
| short | jshort | 有符號,16位 |
| int | jint | 有符號,32位 |
| long | jlong | 有符號,64位 |
| float | jfloat | 32位 |
| double | jdouble | 64位 |
| void | void | 無(wú) |
為了使用方便,還提供了如下定義:
#define JNI_FALSE 0
#define JNI_TRUE 1
Jsize類(lèi)型用于描述主要指數和大?。?/p>
typedef jint jsize;
2.引用類(lèi)型
除了基本類(lèi)型外,JNI還包含了很對對應于不同Java對象的引用類(lèi)型,JNI引用類(lèi)型的組織層次如下圖所示:
在C語(yǔ)言中,所有其他JNI引用類(lèi)型都被定義為與jobject一樣,例如:
typedef jobject jclass;
在C++中,JNI引入虛構類(lèi)以加強子類(lèi)關(guān)系,例如:
class _jobject{};
class _jstring : public jobject{};
…
typedef _jobject jobject;
typedef _jstring jstring;
3.方法ID和域ID
方法ID和域ID是常規的C指針類(lèi)型:
struct_jmethodID; /*不透明結構*/
typedefstruct _jmethodID *jmethodID; /*方法ID*/
struct_jfieldID; /*不透明結構*/
typedefstruct _jfieldID *jfieldID /*域ID*/
4.值類(lèi)型
jvalue聯(lián)合在參數數組中用作單元類(lèi)型,其聲明如下:
typedefunion _jvalue{ jboolean z; jbyte b; jchar c; jshort s; jint i; jlong j; jfloat f; jdouble d; jobject l;}jvalue;5.UTF8字符串
JNI的UTF8字符串與標準UTF8格式有兩個(gè)區別,第一,空字節0使用雙字節格式進(jìn)行編碼,而不是標準UTF8的單字節;第二,只使用單字節、雙字節和三字節格式,不支持更長(cháng)的字節格式。
三、 JNI接口函數命名方式
1. 類(lèi)型簽名
Java虛擬機的類(lèi)型簽名如下:
| 類(lèi)型簽名 | Java類(lèi)型 |
| Z | boolean |
| B | byte |
| C | char |
| S | short |
| I | int |
| J | long |
| F | float |
| D | double |
| Lfully-qulitied-class; | 全限定類(lèi) |
| [type | type[] 數組 |
| (argtypes)rettype | 方法類(lèi)型 |
例如,Java方法int feet(int n, String s,int [] arr)的類(lèi)型簽名如下:
(ILJava/lang/String;[I)I
圓括號里面為參數,I表示第一個(gè)參數int型,LJava/lang/String;表示第二個(gè)參數為全限定Java.lang.String類(lèi)型,[I表示第三個(gè)參數為int型的數組,圓括號后面為返回值類(lèi)型,I表示返回值為int型。
2. 一般函數的JNI接口函數命名方式
一般JNI接口函數命名如下:
Java_包名_類(lèi)名_方法名。
例如:某工程下Sample/test包下MySigal類(lèi)的int GetASample()方法的C語(yǔ)言實(shí)現函數命名如下:
jint Java_Sample_test_MySigal_GetASample(JNIEnv* env,jobjectobj)
其中,包名所包含的“/”應全部以下劃線(xiàn)替代,其本地實(shí)現的參數和返回值也應轉換為JNI類(lèi)型。
3. 重載函數的JNI接口函數命名方式
重載函數的JNI實(shí)現在一般函數的JNI實(shí)現之外,還應添加上類(lèi)型簽名以作為同名函數之間的區別,其接口函數命名如下:
Java_包名_類(lèi)名_方法名_參數簽名。
例如:某工程下Sample/test包下MySigal類(lèi)的int GetASample(int n, String s,int [] arr)方法的C語(yǔ)言實(shí)現函數命名如下:
jintJava_Sample_test_MySigal_GetASample_ILJava_lang_String_2_3I
(JNIEnv*env, jobject obj, jint n, jstring s, jintarray arr)。
JNI在函數命名時(shí)采用名字擾亂方案,以保證所有的Unicode字符都能轉換為有效的C函數名,所有的“/”,無(wú)論是包名中的還是全限定類(lèi)名中的,均使用“_”代替,用_0,…,_9來(lái)代替轉義字符,如下:
| 轉義字符序列 | 表示 |
| _0XXXX | Unicode字符XXXX |
| _1 | 字符“_” |
| _2 | 簽名中的字符“;” |
| _3 | 簽名中的字符“[” |
四、 JNI函數與API
在目前的應用中,我們所主要需要關(guān)心的是C/C++數據類(lèi)型與JNI本地類(lèi)型之間的轉化過(guò)程,這個(gè)過(guò)程某些數據的轉換需要使用JNIEnv對象的一系列方法來(lái)完成。
1.jstring轉換為C風(fēng)格字符串
char* test = (char*)(*env)->GetStringUTFChars(env,jstring,NULL);
使用完畢后,應調用:
(*env)->ReleaseStringUTFChars(env,jstring, test);
釋放資源。
2.C風(fēng)格字符串轉換為jstring
char charStr[50];
jstring jstr;
jstr = env ->NewStringUTF(charStr);
3.C語(yǔ)言中獲取的一段char*的buffer傳遞給Java
在jni中new一個(gè)byte數組,然后使用
(*env)->SetByteArrayRegion(env,bytearray, 0, len, buffer)
操作將buffer拷貝到數組中。
這種方式主要是針對buffer中存在“\0”的情況,如果以C風(fēng)格字符串的方式讀入,就會(huì )損失“\0”之后的字符。
4.數組操作
數組操作的相關(guān)函數列表如下:
| JNI函數 | 功能 |
| GetArrayLength | 返回數組中的元素數 |
| NewObjectArray | 創(chuàng )建一個(gè)指定長(cháng)度的原始數據類(lèi)型數組 |
| GetObjectArrayElement | 返回Object數組的元素 |
| SetObjectArrayElement | 設置Object數組的元素 |
| GetObjectArrayRegion | 將原始數據類(lèi)型數組中的內容拷貝到預先分配好的內存緩存中 |
| SetObjectArrayRegion | 設置緩存中數組的值 |
| ReleaseObjectArrayRegion | 釋放GetObjectArrayRegion分配的內存 |
對int,char等基本數據類(lèi)型的數組操作,將相關(guān)Object名稱(chēng)替換為對應基本數據類(lèi)型名稱(chēng)即為相關(guān)函數。
數組操作的方法選擇基于使用者的需求而定,如果使用者需要在內存中拷貝數組并對其進(jìn)行操作那么一般使用GetObjectArrayRegion和SetObjectArrayRegion函數,否則一般使用SetObjectArrayElement和GetObjectArrayElement函數。
聯(lián)系客服