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