JNI使用規范
一、 JNI概述
JavaNative Interface的縮寫,中文為Java本地調用。從Java1.1開始,JNI即成為Java標準的一部分。
JNI設計的目的是為了允許Java代碼與其他語言進行交互。但這樣做通常會導致喪失平臺可移植性,通常是在特定的需求下進行,例如使用舊的其他語言的庫、需要獲得Java類庫不支持的某種基于具體平臺的特性、大量數學計算性能優化等。
二、 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是常規的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格式有兩個區別,第一,空字節0使用雙字節格式進行編碼,而不是標準UTF8的單字節;第二,只使用單字節、雙字節和三字節格式,不支持更長的字節格式。
三、 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實現之外,還應添加上類型簽名以作為同名函數之間的區別,其接口函數命名如下:
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 |
創建一個指定長度的原始數據類型數組 |
GetObjectArrayElement |
返回Object數組的元素 |
SetObjectArrayElement |
設置Object數組的元素 |
GetObjectArrayRegion |
將原始數據類型數組中的內容拷貝到預先分配好的內存緩存中 |
SetObjectArrayRegion |
設置緩存中數組的值 |
ReleaseObjectArrayRegion |
釋放GetObjectArrayRegion分配的內存 |
對int,char等基本數據類型的數組操作,將相關Object名稱替換為對應基本數據類型名稱即為相關函數。
數組操作的方法選擇基于使用者的需求而定,如果使用者需要在內存中拷貝數組并對其進行操作那么一般使用GetObjectArrayRegion和 SetObjectArrayRegion函數,否則一般使用SetObjectArrayElement和GetObjectArrayElement 函數。