Android NDK與JNI基礎

目錄

什麼是NDK

什麼是JNI

JNI的命名規則

如何實現JNI

JNIEnv指針

JNIEnv的作用

JNIEnv的創建

JNIEnv的釋放

瞭解JNI函數

jobject 與 jclass類型

JNI數據類型

基本類型對照表

引用類型對照表

簽名機制

JNI函數

版本信息

操作類

異常

全局和局部引用

弱全局引用

操作對象

訪問對象成員域

調用對象方法

訪問靜態域

調用靜態方法

操作字符串

操作數組

註冊本地方法

操作監視器(同步鎖)

NIO支持

反射支持

Java虛擬機接口

JNI_OnLoad

JNI_OnUnload


什麼是NDK

Android NDK 是一套工具集合,允許你使用C/C++語言來實現應用程序的部分功能。NDK本身其實就是一個交叉工作鏈,包含了Android上的一些庫文件,然後,NDK爲了方便使用,提供了一些腳本,使得更容易的編譯C/C++代碼。

什麼是JNI

JNI,全稱爲Java Native Interface,即Java本地接口,JNI是Java調用Native 語言的一種特性。通過JNI可以使得Java與C/C++機型交互。即可以在Java代碼中調用C/C++等語言的代碼或者在C/C++代碼中調用Java代碼。

JNI的命名規則

JNIExport jstring JNICALL com_example_hellojni_MainActivity_stringFromJNI(JNIEnv* env, jobject obj) 

jstring                                是返回值類型
com_example_hellojni    是包名
MainActivity                     是類名
stringFromJNI                   是方法名

JNIExport 和 JNICALL 是定義在jni.h頭文件中的宏定義,聲明該JNI函數可從動態庫導出和符合調用約定;
JNIEnv 一個接口指針,是JNI環境結構體指針(C語言版本),使用它能完成很多與java交互的操作;
jobject 表示Java層native方法的調用對象(此處是obj對象)。

如何實現JNI

JNI開發流程的步驟:

  • 第1步:在Java中先聲明一個native方法
  • 第2步:編譯Java源文件javac得到.class文件
  • 第3步:通過javah -jni命令導出JNI的.h頭文件
  • 第4步:使用Java需要交互的本地代碼,實現在Java中聲明的Native方法(如果Java需要與C++交互,那麼就用C++實現Java的Native方法。)
  • 第5步:將本地代碼編譯成動態庫(Windows系統下是.dll文件,如果是Linux系統下是.so文件,如果是Mac系統下是.jnilib)
  • 第6步:通過Java命令執行Java程序,最終實現Java調用本地代碼。

注:javah 是JDK自帶的一個命令,-jni參數表示將class 中用到native 聲明的函數生成JNI 規則的函數。

例:在Terminal下首先定位到src目錄下,然後在輸入

javah -jni com.example.hellojni.MainActivity

使用javah生成Native方法對應的Native函數聲明,會發現所有的Native函數的第一個參數永遠是JNIEnv指針,而第二個參數永遠是jobject或jclass中的一個。JNIEnv指針指代何物?具有何種功能?jobject和jclass又有何區別?

JNIEnv指針

JNIEnv,顧名思義,指代了Java本地接口環境(Java Native Interface Environment),是一個JNI接口指針,指向了本地方法的一個函數表,該函數表中的每一個成員指向了一個JNI函數,本地方法通過JNI函數來訪問JVM中的數據結構。Java可以創建多個線程,每個線程對應一個JNIEnv結構,所以JNI接口指針僅在當前線程中起作用。這意味着指針不能從一個線程進入另一個線程。

JNIEnv的作用

調用Java 函數:JNIEnv代表了Java執行環境,能夠使用JNIEnv調用Java中的代碼

操作Java代碼:Java對象傳入JNI層就是jobject對象,需要使用JNIEnv來操作這個Java對象

JNIEnv的創建

C 中的創建:JNIInvokeInterface是C語言環境中的JavaVM結構體,調用 AttachCurrentThread(JavaVM, JNIEnv*, void) 方法,能夠獲得JNIEnv結構體。

C++中的創建:_JavaVM是C++中JavaVM結構體,調用jint AttachCurrentThread(JNIEnv** p_env, void* thr_args) 方法,能夠獲取JNIEnv結構體。

JNIEnv的釋放

C 中釋放:調用JavaVM結構體JNIInvokeInterface中的(DetachCurrentThread)(JavaVM)方法,能夠釋放本線程的JNIEnv

C++ 中釋放:調用JavaVM結構體_JavaVM中的jint DetachCurrentThread(){ return functions->DetachCurrentThread(this); } 方法,就可以釋放 本線程的JNIEnv

瞭解JNI函數

首先看看什麼是JNI函數。JNI函數就是在native層定義的本地函數,對應於在java層使用native關鍵字聲明的方法的。直白的說,就是在Java層聲明,C/C++語言實現的。當然,這個函數並不一般,它會通過JNI某種機制與Java層的方法進行關聯,使得Java層代碼可以很方便的調用它。

jobject 與 jclass類型

jobject 與 jclass 通常作爲 JNI函數 的第二個參數,當所聲明Native方法是靜態方法時,對應參數jclass,因爲靜態方法不依賴對象實例,而依賴於類,所以參數中傳遞的是一個jclass類型。相反,如果聲明的Native方法時非靜態方法時,那麼對應參數是jobject 。爲了能夠在Native層訪問Java中的類和對象,jobject 和 jclass 分別指代了其所指代的對象和類,進而訪問成員方法和成員變量等。

JNI數據類型

由於Java語言與C/C++語言數據類型的不匹配,需要單獨定義一系列的數據類型轉換關係來完成兩者之間的映射。

基本類型對照表

Java類型 JNI類型 描述
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 無類型

引用類型對照表

Java類型 JNI類型 描述
boolean[] jbooleanArray 布爾類型數組
byte[] jbyteArray 字節數組
char[] jcharArray 字符型數組
short[] jshortArray 短整型數組
int[] jintArray 整型數組
long[] jlongArray 長整型數組
float[] jfloatArray 單精度浮點型數組
double[] jdoubleArray 雙精度浮點型數組
All objects jobject 任何 Java 對象
Object[] jobjectArray 對象數組
java.lang.Class jclass Class 對象
java.lang.String jstring 字符串對象
java.lang.Throwable jthrowable Throwable 對象

簽名機制

函數簽名由字符串組成,第一部分是包含在圓括號()裏的,用來說明參數類型,第二部分則跟的是返回值類型,[表示數組

。比如”([Ljava/lang/Object;)Z”就是參數爲Object[],返回值是boolean的函數的簽名。下表列出類型與簽名標識的對應關係:

Java類型 類型標識
boolean Z
byte B
char C
short S
int I
long J
float F
double D
String L/java/lang/String;
int[] [I
Object[] [L/java/lang/Object;

JNI函數

JNI開發者一定要注意標記爲must的限制。例如,當你看到一個JNI函數表示 must 接受一個不爲null的對象,name你就有義務一定不要傳null給該JNI函數,因爲JNI函數在內部並不會檢查空指針,你需要在調用之前自己檢查。

所有的JNI函數都是通過傳遞進來固定的第一個參數 JNIEnv 參數來進行訪問到的。JNIEnv 類型是一個指向所有JNI函數指針集合的指針。它的定義如下:

typedef const struct JNINativeInteface *JNIEnv;

版本信息

jint GetVersion(JNIEnv *env);

返回JNI的版本號。

參數:

  • env: jni接口指針

返回值:

返回一個值,其中高位爲major版本號返回,低位爲minor版本號。

操作類

jclass DefineClass(JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize bufLen);

從包含二進制的類數據(binary class data)的buffer中載入類。

在調用DefineClass完畢後,虛擬機不會再引用這個buffer,你可以釋放掉它。

參數:

  • env:JNI接口指針
  • name:需要加載的類或接口的短名字,這個字符串使用MUTF-8編碼。
  • loader:指派用來加載類的ClassLoader
  • buf:包含 .class 文件數據的 buffer
  • bufLen: buffer的長度

返回值:

返回加載的Java類對象(java class object),或者如果出現錯誤則返回NULL

拋出異常:

ClassFormatError :如果傳入的class內容不是一個有效的class文件。

ClassCircularityError:如果class或interface是它自己的父類或父接口,造成循環層級關係。

OutOfMemoryError:如果系統在載入的過程中內存不足。

SecurityException :如果調用者啓動載入java包內置的類,引發安全隱患。

注:這個函數不會Android支持,因爲Android並不使用Java字節碼或class文件,所以傳入二進制的class data不會起作用。

jclass FindClass(JNIEnv *env, const char *name);

此函數加載本地定義的類。它在CLASSPATH環境變量指定的目錄和zip文件中搜索具有指定名稱的類。

參數:

  • env:JNI接口指針
  • name:全稱的類名(包名以 / 作爲分隔符, 然後緊跟着類名),如果名字以 [開頭(數組簽名標識符),則返回一個數組的類,這個字符串也是MUTF-8。

返回值:

指定名稱的類的對象(a class object),或者在沒有找到對應類時返回 NULL

拋出異常:

ClassFormatError :如果class內容不是一個有效的class文件。

ClassCircularityError:如果class或interface是它自己的父類或父接口,造成循環層級關係。

OutOfMemoryError:如果系統在載入的過程中內存不足。

NoClassDefFoundError:如果指定的類或接口沒有被找到。(當name傳null或超長時也會拋出這個異常)

jclass GetSuperclass(JNIEnv *env, jclass clazz);

只要傳入的 clazz 參數不是 java/lang/Object 則返回該類的父類。

如果傳入的 clazz 參數是 java/lang/Object 則返回NULL,因爲它沒有父類。當傳入的是一個接口,而不是類時,也返回 NULL

參數:

  • env:JNI接口指針
  • clazz: Java類對象(java class object)

返回值:

返回傳入的 clazz 的父類,或 NULL .

jboolean IsAssignableFrom(JNIEnv *env, jclass class1, jclass clazz2);

檢查 clazz1 的對象是否能被安全的轉型(cast)爲 clazz2

參數:

  • env:JNI接口指針
  • clazz1:第一個class參數(需要轉型的類)
  • clazz2:第二個class參數(轉型的目標類)

返回值:

如果是以下情況則返回 JNI_TRUE :

  • clazz1 和 clazz2 指向同一個java類
  • clazz1 是 clazz2 的子類。(向上轉型是安全的)
  • clazz1 是 clazz2(接口)的實現類。(也屬於向上轉型)

異常

jint Throw(JNIEnv *env, jthrowable obj);

觸發一個 java.lang.Throwable 對象的異常被拋出。

參數:

  • env:JNI接口指針
  • objjava.lang.Throwable 對象

返回值:

成功則返回0, 失敗時返回賦值

拋出異常:

拋出 java.lang.Throwable 對象

jint ThrowNew(JNIEnv *env, jclass clazz, const char *message);

Exception對象的構造器函數,message爲異常的錯誤消息,clazz爲異常的類。

參數:

  • env:JNI接口指針
  • clazzjava.lang.Throwable 的子類
  • message: 用於創建 java.lang.Throwable 對象時傳入的錯誤消息。這個是字符串是MUTF-8編碼。

返回值:

成功則返回0, 失敗時返回賦值

拋出異常:

拋出剛構造出來的 java.lang.Throwable 對象

jthrowable ExceptionOccurred(JNIEnv *env);

檢查是否有異常被拋出。這個異常在本地方法調用 ExceptionClear() 方法或被Java代碼處理這個異常之前都會保持在被拋出狀態。

參數:

  • env:JNI接口指針

返回值:

返回過程中拋出的異常,或沒有異常被拋出時返回 NULL

void ExceptionDescribe(JNIEnv *env);

打印一個異常的stack trace到系統的錯誤輸出,例如 stderr 這是爲了調試提供便利。

參數:

  • env:JNI接口指針
void ExceptionClear(JNIEnv *env);

清理任何即將拋出的異常。如果沒有異常被拋出,則不起任何作用。

參數:

  • env:JNI接口指針
void FatalError(JNIEnv *env, const char *msg);

拋出一個嚴重錯誤,並不希望虛擬機恢復。

參數:

  • env:JNI接口指針
  • msg : 錯誤消息,這個字符串爲MUTF-8編碼
jboolean ExceptionCheck(JNIEnv *env);

這是一個快速函數用於檢查是否有被拋出的異常,而不創建一個這個異常的局部引用。

參數:

  • env:JNI接口指針

返回值:

有一個即將被拋出的異常時返回 JNI_TURE ,沒有則返回 JNI_FALSE

全局和局部引用

局部引用只在本地方法執行的過程有有效。在本地方法返回時它們會被自動釋放掉。所有的局部引用都有消耗一些Java虛擬機的資源。開發者必須確保本地方法不會過度的使用局部引用,儘管局部引用會在本地方法返回到Java後自動釋放,但過度的使用可能會引發虛擬機在執行本地方法時內存不足。

jobject NewGlobalRef(JNIEnv *env, jobject obj);

創建一個 obj 參數對象的全局引用

參數:

  • env :JNI接口指針
  • obj:一個全局或本地的對象引用

返回值:

返回一個全局引用,或者當系統內存不足時返回 NULL

void DeleteGlobalRef(JNIEnv *env, jobject globalRef);

刪除指向 gloablRef 的全局變量。

參數:

  • env :JNI接口指針
  • globalRef: 需要刪除的全局引用
jobject NewLocalRef(JNIEnv *env, jobject ref);

創建一個局部引用同樣指向 ref

參數:

  • env :JNI接口指針
  • ref:可能是一個全局引用,或一個局部引用.

返回值:

返回創建的局部引用,或者如果ref傳入null,則返回 NULL

void DeleteLocalRef(JNIEnv *env, jobject localRef);

刪除 localRef 的局部引用。

參數:

  • env :JNI接口指針
  • localRef:需要刪除的局部引用。
jint EnsureLocalCapacity(JNIEnv *env, jint capacity);

確保在當前線程中至少還有 capacity 個本地引用可能被創建。

在每進入一個本地方法前,Java虛擬機自動確保至少可以上傳 16 個局部引用。

爲了向後兼容,虛擬機在超過 capacity 之後還是可以允許創建本地引用(爲了方便調試,虛擬機會在太多局部引用被創建時會給用戶警告。在JDK裏,開發者可以使用 -verbose:jni 命令行參數來開啓這些警告消息)。虛擬機會在無法創建局部引用時拋出 FatalError

參數:

  • env :JNI接口指針
  • capacity:

返回值:

返回 0 表示還可以創建;否則返回一個負數,並拋出 OutOfMemoryError

jint PushLocalFrame(JNIEnv *env, jint capacity);

創建一個局部引用的棧幀,其可以創建最多 capacity 個局部引用。

注意在之前棧幀中已經創建的局部引用在當前棧幀中依然有效。

參數:

  • env :JNI接口指針
  • capacity:最多可創建的局部引用數

返回值:

成功創建則返回0,失敗則返回一個負數,並拋出 OutOfMenoryError

jobject PopLocalFrame(JNIEnv *env, jobject result);

將當前棧幀出棧,釋放所有局部引用

參數:

  • env :JNI接口指針
  • result: 傳入 NULL 表示不需要返回上一棧幀的引用。

返回值:

返回給定 result 對象的上一個本地引用棧幀本地引用

弱全局引用

弱全局引用是一種特殊的全局引用。和普通全局引用不同的是,一個弱全局引用允許其指向的Java對象可以被垃圾回收。弱全局引用可以在任何可以使用局部引用或全局引用的地方一樣的使用。當垃圾回收器開始回收時,它會將只指向軟引用的對象回收掉。一個弱全局引用指向的被釋放的對象,其功能上相當於 NULL 。開發者可以通過 IsSameObject 來比較一個弱全局引用和 Null 是否相同來檢查其是否指針了一個被釋放的對象。

弱全局引用是Java弱引用(Java Weak References)的一個簡單實現版本,是 Java 2 平臺API的一部分(java.lang.ref 包下)。

澄清(2001年6月新加)

因爲垃圾回收器可能在本地方法執行的同時進行回收,因此被弱全局引用指向的Java對象隨時都可能被釋放掉。當弱全局引用被當做全局引用一樣使用時,會不是很合適,因爲它們可能在沒有通知的情況下就變成和 NULL 一樣的對象了。

雖然 IsSameObject 可以被用於檢查一個弱全局引用是否指向一個被釋放的對象。但它並不能防止對象之後立即被釋放掉。所以,開發者並不能依賴這種檢查來確定在今後的JNI函數調用時這個弱全局對象可用(不是 NULL 引用)。

爲了克服這個侷限性,建議的方法是使用 NewLocalRefNewGlobalRef 函數來創建一個標準的(強)局部或全局引用來指向同一個對象(同弱全局引用指向的那個對象),這些函數會在對象被釋放後返回 NULL ,而沒有被釋放則返回一個強引用(能防止對象被釋放掉)。這個新的引用必須在不需要使用的時候立即明確的刪除掉,來允許該對象允許被釋放。

jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);

創建一個新的弱全局引用

參數:

  • env :JNI接口指針
  • obj:一個全局引用或局部引用對象。

返回值:

返回剛創建的新弱全局引用,如果obj參數爲null或虛擬機內存不足時則返回NULL,如果虛擬機內存不足,還會拋出 OutOfMemoryError

void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);

刪除一個弱引用。

參數:

  • env :JNI接口指針
  • jweak :需要刪除的弱引用

操作對象

jobject AllocObject(JNIEnv *env, jclass clazz);

在不調用類的任何一個構造器來分配一個新的Java對象。

參數:

  • env :JNI接口指針
  • clazz:需要初始化的類,不能是一個數組類型的類。

返回值:

返回新的尚未初始化的Java對象的引用。如果不能分配對象則返回 NULL

拋出異常:

  • InstantiationException 如果傳入的 clazz 是一個抽象類或接口。
  • OutOfMemoryError 如果系統沒有內存時。
jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...);

jobject NewObjectA(JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);

jobject NewObjectV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);

構造(初始化)一個新的Java對象,參數methodID指向需要被調用的構造器函數,這個methodID必須使用 GetMethodID() 來獲取,並且必須使用 <init> 作爲方法名,void(V) 作爲返回類型。

clazz 參數必須不指向一個數組類型的類。

NewObject

可傳入將所有需要傳入給構造器的參數放置在 methodID 參數之後,NewObject函數將接受三個參數,並將其傳入到Java構造器方法內。

NewObjectA:

可將所有需要傳入給構造器的參數列表放置在 jvalue 數組裏,它們會全部傳給Java構造器方法。

NewObjectV:

可將所有構造器參數放置於 va_list 裏,它們也會在調用Java構造器方法時作爲參數傳入。

參數:

  • env :JNI接口指針
  • clazz : 需要初始化對象的類
  • methodID:構造器方法ID
  • const jvalue *args: 構造器參數
  • va_list args : 構造器函數

返回值:

返回一個Java對象,無法被構造時返回 NULL

拋出異常:

  • InstantiationException 如果傳入的 clazz 是一個抽象類或接口。
  • OutOfMemoryError 如果系統沒有內存時。
  • 其他所有構造器可能拋出的異常

 

jclass GetObjectClass(JNIEnv *env, jobject obj);

獲取一個對象的class。

參數:

  • env :JNI接口指針
  • obj:需要獲取class的對象( 必須 不能爲 NULL )

返回值:

一個Java類對象(Java class object)

jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);

返回對象的引用類型,參數obj可能是局部變量,全局變量,或弱全局變量。

參數:

  • env :JNI接口指針
  • obj: 一個局部變量或全局或弱全局引用。

返回值:

如果一下的幾種值(定義爲 jobjectRefType 枚舉值)

  • 無效引用: JNIInvalidRefType = 0
  • 局部引用: JNILocalRefType = 1
  • 全局引用: JNIGlobalRefType = 2
  • 弱全局引用:JNIWeakGlobalRefType = 3

無效引用是沒有指向有效的值。指向的地址不是在內存中分配的地址。

例如 NULL 指向的都是無效引用。

GetObjectRefType 函數的 obj 參數不能是已經刪除的引用。

jboolean IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz);

檢查一個對象是不是一個類的實例。

參數:

  • env :JNI接口指針
  • obj:Java對象
  • clazz:Java類對象(java class object)

返回值:

如果obj可以被cast成clazz,則返回 JNI_TURE ,否則返回 JNI_FALSE

注意 NULL 對象不能cast成任何clazz。

jboolean IsSameObject(JNIEnv *env, jobject ref1, jobject ref2);

檢查兩個引用是否指向同一個Java對象。

參數:

  • env :JNI接口指針
  • ref1: java對象1
  • ref2: java對象2

返回值:

如果指向的是同一個Java對象或兩個都是 NULL,則返回 JNI_TRUE

否則返回 JNI_FALSE

訪問對象成員域

jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

返回一個class的指定成員域的fieldID,成員域根據它的名字和簽名來指定。返回的fieldID可以用來調用 Get<type>FieldSet<type>Field 來訪問具體的成員域。

GetFieldID() 可能會導致一個沒有被初始化(uninitialized)的類被初始化。

題外話:從JVM的源碼來看,這個函數是首先回去嘗試初始化這個類的.

GetFieldId() 不能用來獲取數組的長度,使用 GetArrayLength() 來替代。

參數:

  • env :JNI接口指針
  • clazz :java class object
  • name :成員域的名稱(MUTF-8編碼)
  • sig :成員域的簽名(MUTF-8編碼)

返回值:

返回成員域的fieldID,操作失敗則返回 NULL

拋出異常:

  • NoSuchFieldError 如果沒有指定的字段存在
  • ExceptionInInitializerError 如果在初始化類的過程中出現異常
  • OutOfMemoryError 如果系統內存不足時
jobject GetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID);

jboolean GetBooleanField(JNIEnv *env, jobject obj, jfieldID fieldID);

jbyte GetByteField(JNIEnv *env, jobject obj, jfieldID fieldID);

jchar GetCharField(JNIEnv *env, jobject obj, jfieldID fieldID);

jshort GetShortField(JNIEnv *env, jobject obj, jfieldID fieldID);

jint GetIntField(JNIEnv *env, jobject obj, jfieldID fieldID);

jlong GetLongField(JNIEnv *env, jobject obj, jfieldID fieldID);

jfloat GetFloatField(JNIEnv *env, jobject obj, jfieldID fieldID);

jdouble GetDoubleField(JNIEnv *env, jobject obj, jfieldID fieldID);

這一系統Get方法,用於獲取對象的非靜態成員域。fieldID可以通過 GetFieldId() 函數來獲取。

參數:

  • env :JNI接口指針
  • object:java對象(必須不能爲 NULL
  • fieldId :一個有效的fieldID

返回值:

返回成員域的內容

void SetObjectField(JNIEnv *env, jobject obj, jfieldID fieldID, jobject value);

void SetBooleanField(JNIEnv *env, jobject obj, jfieldID fieldID, jboolean value);

void SetByteField(JNIEnv *env, jobject obj, jfieldID fieldID, jbyte value);

void SetCharField(JNIEnv *env, jobject obj, jfieldID fieldID, jchar value);

void SetShortField(JNIEnv *env, jobject obj, jfieldID fieldID, jshort value);

void SetIntField(JNIEnv *env, jobject obj, jfieldID fieldID, jint value);

void SetLongField(JNIEnv *env, jobject obj, jfieldID fieldID, jlong value);

void SetFloatField(JNIEnv *env, jobject obj, jfieldID fieldID, jfloat value);

void SetDoubleField(JNIEnv *env, jobject obj, jfieldID fieldID, jdouble value);

一組設置基本類型成員域的函數。

參數:

  • env :JNI接口指針
  • object:java對象(必須不能爲 NULL
  • fieldId :一個有效的fieldID
  • value:設置的新值

調用對象方法

jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

返回一個類或接口的非靜態實例方法MethodID,一個方法可以使 clazz 的方法,也可以使 clazz 從其父類繼承而來的方法。這個方法靠方法名或方法簽名來指定。

GetMethodID() 會觸發一個沒有初始化的類被初始化。

通過使用 <init> 作爲方法名,void<V> 作爲返回值來獲取構造器的methodID。

參數:

  • env :JNI接口指針
  • clazz:java class object
  • name:方法名(MUTF-8編碼)
  • sig:方法簽名(MUTF-8編碼)

返回值:

返回方法的MethodID,如果找不到該方法則返回 NULL

拋出異常:

  • NoSucMethodError :如果指定的方法沒有被找到
  • ExceptionInInitializerError: 如果在初始化類的過程中出現異常
  • OutOfMemoryError ; 如果系統內存不足時。
void CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
void CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
void CallVoidMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

jobject CallObjectMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jobject CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jobject CallObjectMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

jboolean CallBooleanMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jboolean CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jboolean CallBooleanMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

jbyte CallByteMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jbyte CallByteMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jbyte CallByteMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

jchar CallCharMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jchar CallCharMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jchar CallCharMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

jshort CallShortMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jshort CallShortMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jshort CallShortMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

jint CallIntMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jint CallIntMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jint CallIntMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

jlong CallLongMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jlong CallLongMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jlong CallLongMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

jfloat CallFloatMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jfloat CallFloatMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jfloat CallFloatMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

jdouble CallDoubleMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jdouble CallDoubleMethodA(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
jdouble CallDoubleMethodV(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);

調用一個Java非靜態實例方法,methodID 根據 GetMethodID() 來獲取。

如果用這些函數來訪問private方法或構造器方法,這MethodID必須來至真正的 obj 類,而不能是它們的父類。

Call<type>Method

可傳入將所有需要傳入給Java方法的參數放置在 methodID 參數之後,NewObject函數將接受三個參數,並將其傳入到Java方法內。

Call<type>MethodA:

可將所有需要傳入給Java方法的參數列表放置在 jvalue 數組裏,它們會全部傳給Java方法。

Call<type>MethodV:

可將Java方法的參數放置於 va_list 裏,它們也會在調用Java方法時作爲參數傳入。

參數:

  • env :JNI接口指針
  • obj : 調用方法的java object
  • methodID:構造器方法ID
  • const jvalue *args: 構造器參數
  • va_list args : 構造器函數

返回值:

返回Java方法的返回結果。

拋出異常:

在執行Java方法時可能拋出的任何異常。

NativeType CallNonvirtual<type>Method(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);

NativeType CallNonvirtual<type>MethodA(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, const jvalue *args);

NativeType CallNonvirtual<type>MethodV(JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, va_list args);

這一系列的方法和上一個系列的方法類似,區別在於 Call<type>Method() 調用方法是基於 jobject ,而 CallNonvirtual<type>Method 調用方式是基於 jclass 對象,通常 jobject 傳入子類對象, jclass 傳入父類class,則可以調用其子類的父類方法,類似於java裏面調用 super.method() 一樣,即在子類裏重寫方法裏可以調用其父類的方法。

訪問靜態域

jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

返回類的靜態域的fieldID,這個靜態域通過字段名和簽名來指定。GetStatic<type>FieldSetStatic<type>Field 一系列訪問函數通過該方法獲得的fieldID來獲取靜態域。

此函數可能引起未初始化(initialized)的類初始化。

參數:

  • env :JNI接口指針
  • clazz :java class object
  • name:靜態域的名稱
  • sig :靜態域的簽名

返回值:

返回fieldID,無法指定的靜態域沒有找到則返回 NULL

拋出異常:

  • NoSuchFieldError :如果指定的靜態域沒有被找到
  • ExceptionInInitializerError:如果類在初始化的過程中出現異常。
jobject GetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID);

jboolean GetStaticBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID);

jbyte GetStaticByteField(JNIEnv *env, jclass clazz, jfieldID fieldID);

jchar GetStaticCharField(JNIEnv *env, jclass clazz, jfieldID fieldID);

jshort GetStaticShortField(JNIEnv *env, jclass clazz, jfieldID fieldID);

jint GetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID);

jlong GetStaticLongField(JNIEnv *env, jclass clazz, jfieldID fieldID);

jfloat GetStaticFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID);

jdouble GetStaticDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID);

一系列函數用於獲取不同類型的靜態域。

參數:

  • env :JNI接口指針
  • class:java class對象
  • fieldId: 靜態域的fieldId.

返回值:

返回靜態域的值。

void SetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value);

void SetStaticBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value);

void SetStaticByteField(JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value);

void SetStaticCharField(JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value);

void SetStaticShortField(JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value);

void SetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID, jint value);

void SetStaticLongField(JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value);

void SetStaticFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value);

void SetStaticDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value);

一系列設置不同類型靜態域的函數。

參數:

  • env :JNI接口指針
  • class:java class對象
  • fieldId: 靜態域的fieldId.
  • value:靜態域的新值

調用靜態方法

jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);

返回類的靜態方法的methodID,靜態方法按方法名和簽名來指定。

該方法可能引發未初始化的方法被初始化。

參數:

  • env :JNI接口指針
  • class:java class對象。
  • name:靜態方法名(MUTF-8編碼字符串)
  • sig:靜態方法簽名(MUTF-8編碼字符串)

返回值:

返回靜態方法的methodID,或操作失敗返回 NULL

拋出異常:

  • NoSucMethodError :如果指定的方法沒有被找到
  • ExceptionInInitializerError: 如果在初始化類的過程中出現異常
  • OutOfMemoryError ; 如果系統內存不足時。
NativeType CallStatic<type>Method(JNIEnv *env, jclass clazz, jmethodID methodID, ...);

NativeType CallStatic<type>MethodA(JNIEnv *env, jclass clazz, jmethodID methodID, jvalue *args);

NativeType CallStatic<type>MethodV(JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);

一系列調用不同類型的靜態方法的函數。

參數:

  • env :JNI接口指針
  • clazz: Java class對象
  • methodId: 靜態方法methodID

返回值:

返回Java靜態方法返回的結果。

操作字符串

jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len);

使用unicode字符串數組來構造一個 java.lang.String 對象。

參數:

  • env :JNI接口指針
  • unicodeChars :指向unicode字符串的指針
  • len : unicode字符串的長度

返回值:

返回一個Java String對象,當字符串無法被構造時返回 NULL

拋出異常:

  • OutOfMemoryError 如果系統內存不足時。
jsize GetStringLength(JNIEnv *env, jstring string);

返回Java String的長度(unicode字符串數組的長度)

參數:

  • env :JNI接口指針
  • string:需要獲取長度的字符串

返回值:

Java String的長度

const jchar * GetStringChars(JNIEnv *env, jstring string, jboolean *isCopy);

返回Java String對象的unicode字符串數組的指針。這個指針一直在調用 ReleaseStringchars() 方法前都有效。

參數:

  • env :JNI接口指針
  • string:目標Java String對象
  • isCopy :NULL或JNI_TRUE表示返回一份copy,JNI_FALSE表示不copy,直接返回指向Java String的原始地址

返回值:

返回unicode 字符串的指針。操作失敗而返回 NULL

void ReleaseStringChars(JNIEnv *env, jstring string, const jchar *chars);

通知虛擬機本地代碼不再需要訪問 chars ,chars參數是一個指針,指向用 string 參數調用 GetStringChars() 方法返回的unicode字符串。

參數:

  • env :JNI接口指針
  • string:一個java字符串對象
  • chars:指向unicode字符串的指針
jstring NewStringUTF(JNIEnv *env, const char *bytes);

根據一個MUTF-8編碼的字符串數組(an array of characters in modified UTF-8 encodin)來構建一個 java.lang.String對象。

參數:

  • env :JNI接口指針
  • bytes:指向MUTF-8編碼字符串的指針。

返回值:

返回一個Java String對象,或失敗時返回 NULL

拋出異常:

  • OutOfMemoryError 如果系統內存不足時。
jsize GetStringUTFLength(JNIEnv *env, jstring string);

返回代表字符串的MUTF-8編碼字符串數組長度(the length in bytes of the modified UTF-8 representation of a string)

參數:

  • env :JNI接口指針
  • string:Java String對象

返回值:

返回字符串的MUTF-8編碼字符串數組的長度。

const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);

返回一個指針,指向代表字符串MUTF-8編碼的字節數組(an array of bytes representin the string in modified UTF-8 encoding)。這個數組的指針一個在調用 ReleaseStrinUTFChars()前有效。

參數:

  • env :JNI接口指針
  • string:目標Java String對象
  • isCopy :NULL或JNI_TRUE表示返回一份copy,JNI_FALSE表示不copy,直接返回指向Java String的原始地址

返回值:

返回指向MUTF-8字符串的指針,或失敗返回 NULL

void ReleaseStringUTFChars(JNIEnv *env, jstring string, const char *utf);

通知Java虛擬機本地代碼不需要訪問 utf 了。這個 utf 是使用 string 調用 GetStringUTFChars 返回的指針。

參數:

  • env :JNI接口指針
  • string:Java String對象
  • utf:指向MUTF-8字符串的指針
void GetStringRegion(JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);

start 偏移量開始拷貝len 個unicode字符到指定的 buf 緩存中。

參數:

  • env :JNI接口指針
  • str: 目標Java String對象
  • start : 偏移量
  • len:拷貝的長度
  • buf : 拷貝到的目標緩存buffer

拋出異常:

  • StringIndexOutOfBoundsException 當指針越界時
void GetStringUTFRegion(JNIEnv *env, jstring str, jsize start, jsize len, char *buf);

start 偏移量開始拷貝len 個unicode字符,將其轉換成MUTF-8編碼,放置到指定的 buf 緩存中。

參數:

  • env :JNI接口指針
  • str: 目標Java String對象
  • start : 偏移量
  • len:拷貝的長度
  • buf : 拷貝到的目標緩存buffer

拋出異常:

  • StringIndexOutOfBoundsException 當指針越界時
const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);

和GetStringChars函數類似,如果可能,Java虛擬機會返回內部指向字符串元素的指針,否則返回一個複製值。但不管怎樣,GetStringCriticalReleaseStringCritical 這些函數的使用必須非常小心,在這兩個函數之間,本地代碼必須不能調用任何可能阻塞當前線程的JNI函數。

參數:

  • env :JNI接口指針
  • jstring : 目標Java字符串對象
  • isCopy :是否複製值。

返回值:

虛擬機內部指向字符串元素的指針。

void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);

釋放由 GetStringCritical 函數調用獲得的字符串指針。

參數:

  • env :JNI接口指針
  • string:字符串對象
  • carray: 獲取到的字符串指針。

操作數組

jsize GetArrayLength(JNIEnv *env, jarray array);

返回數組(array)的元素個數。

參數:

  • env :JNI接口指針
  • array:目標Java數組對象

返回值:

返回數組的長度

jobjectArray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);

構造一個新的 elementClass 類型的數組,並設置其初始值。

參數:

  • env :JNI接口指針
  • length:數組長度
  • elementClass :數組元素的class
  • initialElement:初始化數組元素

返回值:

返回一個Java數組對象,如果數組不能被構造則返回 NULL

拋出異常:

  • OutOfMemoryError :如果系統內存不足時。
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index);

返回數組的index處的元素對象。

參數:

  • env :JNI接口指針
  • array:Java數組
  • index:數組下標

返回值:

返回一個Java對象。

拋出異常:

  • ArrayIndexOutOfBoundsException: 如果index數組越界時。
void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);

設置數組的某個值。

參數:

  • env :JNI接口指針
  • array:一個Java數組
  • index:數組下標
  • value:設置的新值

拋出異常:*

  • ArrayIndexOutOfBoundsException ,如果 index 數組越界時。
  • ArrayStoreException ,如果 value 類型錯誤時。
jbooleanArray NewBooleanArray(JNIEnv *env, jsize length);

jbyteArray NewByteArray(JNIEnv *env, jsize length);

jcharArray NewCharArray(JNIEnv *env, jsize length);

jshortArray NewShortArray(JNIEnv *env, jsize length);

jintArray NewIntArray(JNIEnv *env, jsize length);

jlongArray NewLongArray(JNIEnv *env, jsize length);

jfloatArray NewFloatArray(JNIEnv *env, jsize length);

jdoubleArray NewDoubleArray(JNIEnv *env, jsize length);

一組函數用於構造各種不同基本類型的數組對象。

參數:

  • env :JNI接口指針
  • length:數組長度

返回值:

返回Java數組,數組無法被構造則返回 NULL

jboolean GetBooleanArrayElements(JNIEnv *env, jbooleanArray array, jboolean *isCopy);

jbyte GetByteArrayElements(JNIEnv *env, jbyteArray array, jboolean *isCopy);

jchar GetCharArrayElements(JNIEnv *env, jcharArray array, jboolean *isCopy);

jshort GetShortArrayElements(JNIEnv *env, jshortArray array, jboolean *isCopy);

jint GetIntArrayElements(JNIEnv *env, jintArray array, jboolean *isCopy);

jlong GetLongArrayElements(JNIEnv *env, jlongArray array, jboolean *isCopy);

jfloat GetFloatArrayElements(JNIEnv *env, jfloatArray array, jboolean *isCopy);

jdouble GetDoubleArrayElements(JNIEnv *env, jdoubleArray array, jboolean *isCopy);

一組函數用於獲取各種不同基本類型數組的元素。

返回的結構在調用相應的 Release<Type>ArrayElements() 之前一直有效。因爲返回的可能是一個複製值,因此對於返回值的修改不一定會對原始數組產生影響,直到調用了相應的 Release<Type>ArrayElements() 纔可能影響到原始數組。

參數:

  • env :JNI接口指針
  • array:目標數組
  • isCopy: NULLJNI_FALSE 不COPY,JNI_TRUE 返回COPY值。

返回值:

返回數組某個元素的指針。或失敗返回 NULL

void ReleaseBooleanArrayElements(JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode);

void ReleaseByteArrayElements(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode);

void ReleaseCharArrayElements(JNIEnv *env, jcharArray array, jchar *elems, jint mode);

void ReleaseShortArrayElements(JNIEnv *env, jshortArray array, jshort *elems, jint mode);

void ReleaseIntArrayElements(JNIEnv *env, jintArray array, jint *elems, jint mode);

void ReleaseLongArrayElements(JNIEnv *env, jlongArray array, jlong *elems, jint mode);

void ReleaseFloatArrayElements(JNIEnv *env, jfloatArray array, jfloat *elems, jint mode);

void ReleaseDoubleArrayElements(JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode);

一組函數通知虛擬機本地代碼不再需要訪問數組中的元素。這些元素是使用 Get<type>ArrayElements函數來獲取的,如果需要,這個函數會把修改後的內容複製到原始的數組中。使用最後一個參數 mode 來控制:

  • mode = 0 , 將內容複製回原始數組,並釋放 elems (通常情況下都傳入0即可)
  • mode = JNI_COMMIT ,將內容複製回原始數組,但不釋放 elems
  • mode = JNI_ABORT ,不將內容複製回原始數組,並釋放 elems

參數:

  • env :JNI接口指針
  • array:目標數組
  • elems: 指向數組元素的指針
  • mode:釋放模式,0 或 JNI_COMMIT 或 JNI_ABORT
void GetBooleanArrayRegion(JNIEnv *env, jbooleanArray array, jsize start, jsize len,  jboolean *buf);

void GetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len,  jbyte *buf);

void GetCharArrayRegion(JNIEnv *env, jcharArray array, jsize start, jsize len,  jchar *buf);

void GetShortArrayRegion(JNIEnv *env, jshortArray array, jsize start, jsize len,  jshort *buf);

void GetIntArrayRegion(JNIEnv *env, jintArray array, jsize start, jsize len,  jint *buf);

void GetLongArrayRegion(JNIEnv *env, jlongArray array, jsize start, jsize len,  jlong  *buf);

void GetFloatArrayRegion(JNIEnv *env, jfloatArray array, jsize start, jsize len,  jfloat *buf);

void GetDoubleArrayRegion(JNIEnv *env, jdoubleArray array, jsize start, jsize len,  jdouble *buf);

一組函數用來複制基本類型數據的一部分值到buffer。

參數:

  • env :JNI接口指針
  • array:java數組
  • start :起始的下標
  • len:需要複製的元素個數
  • buf:目標buffer

拋出異常:

  • ArrayIndexOutOfBoundsException :如果數組越界時。
void SetBooleanArrayRegion(JNIEnv *env, jbooleanArray array, jsize start, jsize len, const jboolean *buf);

void SetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf);

void SetCharArrayRegion(JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf);

void SetShortArrayRegion(JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf);

void SetIntArrayRegion(JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf);

void SetLongArrayRegion(JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong  *buf);

void SetFloatArrayRegion(JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf);

void SetDoubleArrayRegion(JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf);

一組從buffer複製回基本類型數組的函數。

參數:

  • env :JNI接口指針
  • array:java數組
  • start :起始的下標
  • len:需要複製的元素個數
  • buf:數據源buffer

拋出異常:

  • ArrayIndexOutOfBoundsException :如果數組越界時。
void * GetPrimitiveArrayCritical(JNIEnv *env, jarray array, jboolean *isCopy);

Get<type>ArrayElements 函數類似,但如果可能的話,Java虛擬機會返回一個基本類型數組的原始指針,否則返回一個copy值得指針。

GetPrimitiveArrayCriticalReleasePrimitiveArrayCritical 這些函數的使用必須非常小心,在這兩個函數之間,本地代碼必須不能調用任何可能阻塞當前線程以等待其他Java線程的JNI函數,或任何系統調用。(例如,當前線程不能 read一個另一個Java線程正在寫入的流。)

參數:

  • env :JNI接口指針
  • array:目標數組
  • isCopyJNI_TRUE複製值,JNI_FALSE不復制。
void ReleasePrimitiveArrayCritical(JNIEnv *env, jarray array, void *carray, jint mode);

釋放由 GetPrimitiveArrayCritical函數獲取的數組原始指針。

參數:

  • env :JNI接口指針
  • array:目標數組
  • carray: 之前獲取的原始數組指針
  • mode0JNI_COMMITJNI_ABORT

註冊本地方法

jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);

給clazz註冊一個本地方法。

參數:

  • env :JNI接口指針
  • clazz:指定的java class對象
  • methods:原生方法列表
  • nMethods: 原生方法列表數量

返回值:

成功返回0, 失敗返回負數

拋出異常:

  • NoSuchMethodError: 如果指定的方法沒有找到,或者方法不是native。
jint UnregisterNatives(JNIEnv *env, jclass clazz);

註銷一個類的所有本地方法,這個類會回到之前被鏈接或註冊本地方法之前的狀態。

這個函數不應該在一般的本地代碼中使用。

參數:

  • env :JNI接口指針
  • clazz:java class對象

返回值:

成功返回0, 失敗返回負數。

操作監視器(同步鎖)

jint MonitorEnter(JNIEnv *env, jobject obj);

進入一個obj的監視區monitor。

即使用 obj 作爲鎖對象(可能是對象鎖,也可能是類鎖),obj必須不能爲 null。

每個java對象內部都有一個鎖(monitor),如果當前線程已經擁有了obj的monitor,它會在其監視區增加計數器,記錄線程進入監視區的次數。如果obj上的監視區沒有被任何其他線程持有,則當前線程開始持有監視區的鎖,設置監視區的計數器爲1;如果其他線程已經擁有了這個監視區的鎖,則當前線程一直等待鎖被釋放,然後再嘗試獲取鎖。

通過 MonitorEnter 這個JNI函數進入到一個監視區中,是無法使用Java虛擬機 monitorexit 指令或一個同步方法返回來退出這個監視區的。MonitorEnter函數和Java虛擬機 monitorexit 指令之間可能搶着進入同一個obj的監視區。

爲了避免死鎖,使用 MonitorEnter方法來進入監視區,必須使用MonitorExit 來退出,或調用 DetachCurrentThread 來明確釋放JNI監視區。

參數:

  • env :JNI接口指針
  • obj:一個普通的java對象或class對象。

返回值:

成功返回0,失敗返回負數。

jint MonitorExit(JNIEnv *env, jobject obj);

當前線程必須持有obj的monitor,當前線程進入monitor的計數器減1,如果計數器的值變爲了0,則當前線程釋放monitor。

本地代碼不能使用 MonitorExit 函數來退出一個通過Java虛擬機 monitorexit指令或一個同步方法進入的monitor。

參數:

  • env :JNI接口指針
  • obj:一個java對象或一個class對象

返回值:

成功返回0,失敗返回負數。

排除異常:

  • IllegalMonitorStateException如果當前線程還沒持有monitor。

NIO支持

jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity);

分配並返回一個 java.nio.ByteBuffer ,指向一塊內存,地址開始於 address ,並有 capacity 個bytes的內存空間。

本地代碼調用這個函數並生成byte-buffer對象給一個Java層,必須確保這個buffer指向一個有效的可讀內存空間,如果需要,也得是可寫的內存。如果從Java代碼嘗試訪問一個無效的內存空間,可能返回一個隨機的值,或沒有任何可見反應,或拋出一個未指定的異常。

JDK/JRE 1.4及以上支持該函數。

參數:

  • env :JNI接口指針
  • address:內存空間起始地址(必須不爲 NULL )
  • capacity:內存空間的大小(字節數)(必須爲整數)

返回值:

返回剛創建的 java.nio.ByteBuffer 對象的局部引用。如果有異常拋出或者虛擬機不允許JNI訪問直接的buffer,則返回 NULL

拋出異常:

  • OutOfMemoryError: 如果分配 ByteBuffer 對象失敗時。
void* GetDirectBufferAddress(JNIEnv* env, jobject buf);

獲取給定的 java.nio.Buffer 的起始內存地址。

這個函數通過buffer對象允許本地代碼訪問Java代碼可訪問的同一塊內存地址。

JDK/JRE 1.4及以上支持該函數。

參數:

  • env :JNI接口指針
  • buf:給定的 java.nio.Buffer 對象(必須不能爲 NULL )

返回值:

返回buffer指向的起始內存地址。如果內存地址是undefined的,如果給定的jobject不是 java.nio.Buffer 或者如果虛擬機不支持JNI訪問直接的buffer,則返回 NULL

jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf);

獲取並返回給定 java.nio.Buffer 對象的內存空間大小(capacity)。capacity是內存空間包含的元素的個數(the number of elements that the memory region contains)

參數:

  • env :JNI接口指針
  • buf : 給定的buffer對象。(必須不能爲 NULL )

返回值:

返回buffer對應的內存空間的capacity。如果給定的jobject不是 java.nio.Buffer ,或者the object is an unaligned view buffer and the processor architecture does not support unaligned access,或者虛擬機不允許JNI訪問direct buffer,則返回 -1

反射支持

開發者如果知道方法或域的名稱和類型,可以使用JNI來調用Java方法或訪問Java域。Java反射API提供運行時自省。JNI提供一組函數來使用Java反射API。

jmethodID FromReflectedMethod(JNIEnv *env, jobject method);

通過 java.lang.reflect.Methodjava.lang.reflect.Constructor 來獲取其反射的目標方法對應的 methodId.

參數:

  • env :JNI接口指針
  • jobject:一個 java.lang.reflect.Methodjava.lang.reflect.Constructor 對象。

返回值:

反射的目標方法的methodID

jfieldID FromReflectedField(JNIEnv *env, jobject field);

通過 java.lang.reflect.Field 對象來獲取fieldID.

參數:

  • env :JNI接口指針
  • fieldjava.lang.reflect.Field 對象

返回值:

目標成員域的fieldID

jobject ToReflectedMethod(JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);

轉換 cls 的 methodID 爲 java.lang.reflect.Methodjava.lang.reflect.Constructor對象。

參數:

  • env :JNI接口指針
  • cls:目標方法的java類對象
  • jmethodId :目標方法的methodID
  • isStatic:目標方法爲靜態的則比較將 isStatic 設置爲 JNI_TRUE ,否則設置爲 JNI_FALSE

返回值:

java.lang.reflect.Methodjava.lang.reflect.Constructor對象

jobject ToReflectedField(JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic);

轉換 cls 的 fieldID 爲 java.lang.reflect.Field 對象。

參數:

  • env :JNI接口指針
  • cls:目標方法的java類對象
  • jmethodId :目標方法的methodID
  • isStatic:目標方法爲靜態的則比較將 isStatic 設置爲 JNI_TRUE ,否則設置爲 JNI_FALSE

返回值:

java.lang.reflect.Field 對象

Java虛擬機接口

jint GetJavaVM(JNIEnv *env, JavaVM **vm);

返回關聯到當前線程的Java虛擬機接口指針。

參數:

  • env :JNI接口指針
  • vm: 用於存放返回的Java虛擬機指針

返回值:

成功返回0,失敗返回負數。

JNI_OnLoad

Java虛擬機在加載本地庫(native library)時(即調用 System.loadLibrary() )後,在加載本地庫到內存之後,會尋找其內部的 JNI_OnLoad 函數,並執行它。這個函數必須返回本地庫使用的 JNI版本號。

如果要使用新的JNI函數,則必須返回高版本的JNI版本號。如果本地庫不提供 JNI_ONLoad 函數,則虛擬機默認它使用的是 JNI_VERSION_1_1版本。如果返回一個虛擬機不支持的JNI版本號,則本地庫不能被加載。

JNI_OnUnload

虛擬機在本地庫被垃圾回收前,調用其 JNI_OnUnload 函數。這個函數用來執行一個清理操作。因爲函數在不確定的情況下被調用(例如 finalizer),因此開發者應該保守的使用Java虛擬機服務,並避免任何Java回調函數。

注意:JNI_OnLoadJNI_OnUnload 兩個函數是可選的,不是必須要有。

參考連接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章