我們知道,利用javah生成的c/c++頭文件的時候,會對java中定義的 native 函數生成對應的jni層函數,如下:
/*
* Class: com_lms_jni_JniTest
* Method: getTestString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_lms_jni_JniTest_getTestString
(JNIEnv *, jobject);
我們可以看到方法名是以Java_com_lms_jni等開頭的,還有什麼所謂的Signature,那這些其實都是什麼意思呢,今天我們就來簡單地認識一下。
JNI 命名規則
對於傳統的JNI編程來說,JNI方法跟Java類方法的名稱之間有一定的對應關系,要遵循一定的命名規則,如下:
1) 前綴: Java_
2) 類的全限定名,用下劃線進行分隔(_):com_lms_jni_JniTest
3) 方法名:getTestString
3) jni函數指定第一個參數: JNIEnv *
4) jni函數指定第二個參數: jobject
5) 實際Java參數: jstring, jint ....
6) 返回值的參數 : jstring, jint....
所以對於在Java類 com.lms.jni.HwDemo中的一個方法:
public native String addTail(String tail);
其對應的jni層的方法如下:
jstring Java_com_lms_jni_HwDemo_addTail(JNIEnv * e, jobject clazz, jstring tail);
如果不這樣命名,當把動態庫加載進DVM的時候,通過JNIEnv *指針去查找Java Native方法對應的JNI方法的時候,就會找不到了。
注意,我們也可以利用函數注冊的方法,將Java層的方法名跟JNI層的方法名的對應關系保存起來,注冊到DVM中,就不需要這樣的命名規范了。
JNI 數據類型
我們知道Java的數據類型是跟C/C++的數據類型是不一樣的,而JNI是處於Java和Native本地庫(大部分是用C/C++寫的)中間的一層,JNI對於兩種不同的數據類型之間必須做一種轉換,所以在JNI跟Java之間就會有數據類型的對應關系。
在JNI中,提供了以下各種數據類型,可以分為原生類型和引用類型:
對於原生類型有:jchar, jbyte, jshort, jint, jlong, jfloat, jdouble, jboolean,其與java端的數據類型對應如下表:
java
jni
char
jchar
byte
jbyte
short
jshort
int
jint
long
jlong
float
jfloat
double
jdouble
boolean
jboolean
對於引用類型則有:jobject, jstring, jthrowable, jclass, jarray, 以及繼承於jarray,對應於其原生類型的8種j
array和jobjectarray。
知道了不同的數據類型的轉換關系,我們就知道在什麼情況下,應該對數據進行怎麼樣的處理。
JNI方法簽名
為什麼會有方法簽名這種東西呢?這是因為Java這邊支持函數重載,即雖然參數不一樣,但是方法名一樣,那麼在JNI層它們的方法名都會是一樣的,那JNI也會犯迷糊了,得找哪個呢?
不過也正是因為其參數類型是不一樣的,所以就出現了方法簽名,利用方法簽名和方法名來唯一確定一個JNI函數的調用。
既然方法簽名是基於參數類型的不同而形成的,首先要知道Java各數據類型對應的簽名是什麼,也就是所謂的類型簽名,
在jni.h文件中就已經定義了這樣一套規則,如下:
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
對應於Java端的數據類型,我們也可以看一下下面的表:
Java 類型
類型簽名
boolean
Z
byte
B
char
C
short
S
int
I
long
L
float
F
double
D
類
L全限定名;,比如String, 其簽名為Ljava/lang/util/String;
數組
[類型簽名, 比如 [B
對於上面的類,要注意其後面還有一個分號。
而對一個方法,其簽名就是其參數類型簽名和返回值類型簽名的字符串,其形式如下:
(類型簽名1類型簽名2...)返回值類型簽名
每個類型簽名之間是沒有空格的,下面看看兩個例子:
有方法 1):
public string addTail(String tail, int index)
其對應的簽名如下:
(Ljava/util/String;I)Ljava/util/String;
方法 2):
public int addValue(int index, String value,int[] arr)
其對應的簽名如下:
(ILjava/util/String;[I)I
相信通過這兩個例子,大家也能夠了解了方法簽名是什麼樣的形式了吧,對於JNI這些奇形怪狀的表示形式也有一定的了解了。
結束。