JNI中的數據類型以及數據結構 —— 這些結構存在的意義

JNI中的數據類型以及數據結構

一、概述

因爲JNI是一套Native層和JVM通信的機制,通信就是信息交流,所以就必須要提供在兩種語言間的數據交換的機制。基於這個原因,JNI中通過typedef定義了一套專用的數據類型以及數據結構。

二、Java基本類型

Java Type Native Type Description
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
void void not applicable

還添加了一下定義方便使用

  • #define JNI_FALSE 0
  • #define JNI_TRUE 1
  • typedef jint jsize 用於標識索引和大小(C++標準建議用盡量準確的類型符來標識一個變量。比如,表示大小就用size_t等,而不是使用在個平臺都有可能產生不一致的int、short等)

三、Java引用類型

JNI 中還包含了一組特殊的類型,用來對應Java中的引用類型,我們暫且稱之爲JNI引用類型。爲了儘量和Java引用類型像照應,JNI引用類型遵照如下層次結構:

  • jobject(用於引用所有Object對象)
    • jclass(用於引用Classs對象)
    • jstring(用於引用String對象)
    • jarray(數組對象)
    • jobjectArray(對象數組)
    • jbooleanArray(boolean數組,不是Boolean數組)
    • jbyteArray(byte數組)
    • jcharArray(char數組)
    • jshortArray(short數組)
    • jintArray(int數組)
    • jlongArray()
    • jfloatArray()
    • jdoubleArray()

JNI在定義這一組JNI引用類型的時候,正對C和C++分別採取了不同的定義方式。C中採用typedef jobject jclass;這種定義。C++因爲是面向對象的,所以直接採用了類繼承的方式。詳見下圖

JNI引用類型

可以看到在C中,不管是什麼類型本質上都是void* 類型,而C++則是空類並且有了繼承關係。可本質上並沒有什麼區別,使用的時候具體是什麼類型仍需要用戶自己去區別。

四、Java字段和方法(Field/Method)

Java中的字段和方法定義爲C中結構體的指針,如下:

struct _jfieldID;              /* opaque structure */
typedef struct _jfieldID *jfieldID;   /* field IDs */

struct _jmethodID;              /* opaque structure */
typedef struct _jmethodID *jmethodID; /* method IDs */

jfieldID以及jmethodID後面註釋了一句 “opaque structure(不透明結構)”,應該是說該結構交給JVM自己去定義。jni.h文件中還定義了JNINativeMethod類型,該類型暫時不用關注,它用於標識一個Java方法(一般在Native方法與Java方法的動態綁定時使用)。

五、JNI Value類型(聯合體類型)

typedef union jvalue {
    jboolean z;
    jbyte    b;
    jchar    c;
    jshort   s;
    jint     i;
    jlong    j;
    jfloat   f;
    jdouble  d;
    jobject  l;
} jvalue;

六、JNI中的類型簽名

在JNI中個JVM通信時很多時候不使用具體類型(比如boolean、int、class等)而是使用類型簽名,並且是直接使用JVM所使用的類型簽名(應該是處於效率之類的考慮吧)。JVM中的類型簽名如下:

類型簽名 具體Java類型
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L 接上具體Class的全限定名 ; 具體的Class類型
[ 接上類型簽名 數組類型
(參數簽名)方法返回值類型簽名 方法類型簽名

舉使用類型簽名的一個例子:

一個Java方法:
long f(int n,String s,int[] arr);
其類型簽名如下:
(ILjava/lang/String;[I)J

  1. 注意其中的信息:Iint 的類型簽名,Ljava/lang/String;String 的類型簽名,[Iint[] 的類型簽名,J是long的類型簽名。* 其合在一起是long f(int n,String s,int[] arr);*這個方法的類型簽名。

  2. Java中有方法簽名來標識方法的唯一性,那麼就有類型簽名來標識類型的唯一性(類型簽名就是給boolean、int、class等這些找了一個簡稱或者別稱)。

  3. 其中最後一個方法類型簽名,在java.lang.invoke包中有一個MethodType類型,該類型屬於Java方法簽名的一部分,包含方法的參數信息以及返回值信息。我們可以理解爲,方法包含類型信息以及方法名信息(類似參數包含參數名以及類型信息)。

七、JNI中UTF-8字符串說明

JNI中使用的字符編碼方案是在UTF-8的基礎上稍加修改而來的,具體的查看JNI文檔(JVM中採用的也是修改過的UTF-8)。

其中涉及到兩點不同:

  1. 對空字符(char)0編碼時不是像標準UTF-8那樣使用一個字節,而是使用兩個字節,這就避免了在字符串中嵌入了空值(應該是避免了無意間引入的空字符對JVM的運行產生影響)。
  2. Java VM 無法識別UTF-8的四字節格式,它使用兩倍或三倍三字節來代替UTF-8中超過三字節的表示。

八、字符編碼說明

因爲早前搞OpenVPN的時候仔細去熟悉了下Unicode纔算明白Unicode以及UTF-8/UTF-16之間的淵源,簡單的提一下。

Unicode(統一碼、萬國碼、單一碼),是計算機行業定義的一套字符標準。一般我們提到的時候會說Unicode字符集,講的是一套字符集,而不是一套編碼方案,並且它引入了平面的概念。不講複雜碼位平面什麼的,簡單點說Unicode字符集用四個字節來指向一個字符,這是字符標準,在你電腦的字體文件中就是通過Unicode的碼位來一一照應上文字(不是通過UTF-8或者其他編碼方案的值)。而UTF-8/UTF-16等都編碼方案,將四個字節的標準Unicode碼編碼成UTF-8等所定義的編碼格式,但是真正用的時候還需要解碼回Unicode。之所以這樣做,是因爲Unicode每個字符都佔用4個字節,但常用字符往往沒那麼多,重新實現編碼方案將大大的縮小空間浪費。

九、JNI爲什麼定義這些信息

JNI是JVM和Native層溝通的橋樑,要達到JVM和Native可以相互溝通的目的,就必須實現下面兩點

  1. Java中可以調用Native層的方法(不可避免的需要傳遞參數),持有Native層數據結構或者對象的引用,保存Native層的變量。
  2. Native同樣需要可以調用Java層的方法,持有Java層對象的引用,保存Java層變量。

1、基於方法調用

  1. 在Java層提供了native方法屬性,被該屬性標記的方法可以和Native層方法產生關聯用來代用Native層方法。關聯方式有:
    • 靜態綁定(通過命名方式綁定)
    • 動態綁定(通過手動寫綁定方法綁定,後面會有)
  2. 在Native層呢,就是通過上面的jclass、jmethod等以及方法類型簽名來調用。

2、相互保存對方變量以及持有對方引用

  1. Java層持有Native層對象一般很好解決,真毒C/C++來說,直接保存其內存地址或者內存數據即可。
  2. Native層只有Java層對象只能通過jclass、jstring、jobjectArray等來持有對象的引用。數據的保存,比如byte[]等數組類型除了持有引用外還提供一些額外的操作,像copy等,後面會有介紹。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章