1. 程式人生 > >Android JNI 學習(四):JNI 介面整理 — 方法表 & Base Api & Exception Api

Android JNI 學習(四):JNI 介面整理 — 方法表 & Base Api & Exception Api

本文我們來總結一下JNI 提供的功能列表及相關的函式表。

注意:請注意使用術語“必須”來描述對JNI程式設計師的限制。例如,當您看到某個JNI函式必須接收非NULL物件時,您有責任確保不將NULL傳遞給該JNI函式。因此,JNI實現不需要在該JNI函式中執行NULL指標檢查。

一、介面方法表

可以通過JNIEnv引數以固定偏移量訪問每個函式JNIEnv型別是一個指向儲存所有JNI函式指標的結構。它的定義如下:

typedef const struct JNINativeInterface * JNIEnv;

VM初始化功能表,如下面的程式碼所示 請注意,前三個條目是保留用於將來與COM的相容性的。

此外,我們NULL在函式表的開頭附近保留了許多附加條目,因此,例如,可以在FindClass之後而不是在表的末尾新增與類相關的未來JNI操作。

const struct JNINativeInterface ... = {

    NULL,
    NULL,
    NULL,
    NULL,
    GetVersion,

    DefineClass,
    FindClass,

    FromReflectedMethod,
    FromReflectedField,
    ToReflectedMethod,

    GetSuperclass,
    IsAssignableFrom,

    ToReflectedField,

    Throw,
    ThrowNew,
    ExceptionOccurred,
    ExceptionDescribe,
    ExceptionClear,
    FatalError,

    PushLocalFrame,
    PopLocalFrame,

    NewGlobalRef,
    DeleteGlobalRef,
    DeleteLocalRef,
    IsSameObject,
    NewLocalRef,
    EnsureLocalCapacity,

    AllocObject,
    NewObject,
    NewObjectV,
    NewObjectA,

    GetObjectClass,
    IsInstanceOf,

    GetMethodID,

    CallObjectMethod,
    CallObjectMethodV,
    CallObjectMethodA,
    CallBooleanMethod,
    CallBooleanMethodV,
    CallBooleanMethodA,
    CallByteMethod,
    CallByteMethodV,
    CallByteMethodA,
    CallCharMethod,
    CallCharMethodV,
    CallCharMethodA,
    CallShortMethod,
    CallShortMethodV,
    CallShortMethodA,
    CallIntMethod,
    CallIntMethodV,
    CallIntMethodA,
    CallLongMethod,
    CallLongMethodV,
    CallLongMethodA,
    CallFloatMethod,
    CallFloatMethodV,
    CallFloatMethodA,
    CallDoubleMethod,
    CallDoubleMethodV,
    CallDoubleMethodA,
    CallVoidMethod,
    CallVoidMethodV,
    CallVoidMethodA,

    CallNonvirtualObjectMethod,
    CallNonvirtualObjectMethodV,
    CallNonvirtualObjectMethodA,
    CallNonvirtualBooleanMethod,
    CallNonvirtualBooleanMethodV,
    CallNonvirtualBooleanMethodA,
    CallNonvirtualByteMethod,
    CallNonvirtualByteMethodV,
    CallNonvirtualByteMethodA,
    CallNonvirtualCharMethod,
    CallNonvirtualCharMethodV,
    CallNonvirtualCharMethodA,
    CallNonvirtualShortMethod,
    CallNonvirtualShortMethodV,
    CallNonvirtualShortMethodA,
    CallNonvirtualIntMethod,
    CallNonvirtualIntMethodV,
    CallNonvirtualIntMethodA,
    CallNonvirtualLongMethod,
    CallNonvirtualLongMethodV,
    CallNonvirtualLongMethodA,
    CallNonvirtualFloatMethod,
    CallNonvirtualFloatMethodV,
    CallNonvirtualFloatMethodA,
    CallNonvirtualDoubleMethod,
    CallNonvirtualDoubleMethodV,
    CallNonvirtualDoubleMethodA,
    CallNonvirtualVoidMethod,
    CallNonvirtualVoidMethodV,
    CallNonvirtualVoidMethodA,

    GetFieldID,

    GetObjectField,
    GetBooleanField,
    GetByteField,
    GetCharField,
    GetShortField,
    GetIntField,
    GetLongField,
    GetFloatField,
    GetDoubleField,
    SetObjectField,
    SetBooleanField,
    SetByteField,
    SetCharField,
    SetShortField,
    SetIntField,
    SetLongField,
    SetFloatField,
    SetDoubleField,

    GetStaticMethodID,

    CallStaticObjectMethod,
    CallStaticObjectMethodV,
    CallStaticObjectMethodA,
    CallStaticBooleanMethod,
    CallStaticBooleanMethodV,
    CallStaticBooleanMethodA,
    CallStaticByteMethod,
    CallStaticByteMethodV,
    CallStaticByteMethodA,
    CallStaticCharMethod,
    CallStaticCharMethodV,
    CallStaticCharMethodA,
    CallStaticShortMethod,
    CallStaticShortMethodV,
    CallStaticShortMethodA,
    CallStaticIntMethod,
    CallStaticIntMethodV,
    CallStaticIntMethodA,
    CallStaticLongMethod,
    CallStaticLongMethodV,
    CallStaticLongMethodA,
    CallStaticFloatMethod,
    CallStaticFloatMethodV,
    CallStaticFloatMethodA,
    CallStaticDoubleMethod,
    CallStaticDoubleMethodV,
    CallStaticDoubleMethodA,
    CallStaticVoidMethod,
    CallStaticVoidMethodV,
    CallStaticVoidMethodA,

    GetStaticFieldID,

    GetStaticObjectField,
    GetStaticBooleanField,
    GetStaticByteField,
    GetStaticCharField,
    GetStaticShortField,
    GetStaticIntField,
    GetStaticLongField,
    GetStaticFloatField,
    GetStaticDoubleField,

    SetStaticObjectField,
    SetStaticBooleanField,
    SetStaticByteField,
    SetStaticCharField,
    SetStaticShortField,
    SetStaticIntField,
    SetStaticLongField,
    SetStaticFloatField,
    SetStaticDoubleField,

    NewString,

    GetStringLength,
    GetStringChars,
    ReleaseStringChars,

    NewStringUTF,
    GetStringUTFLength,
    GetStringUTFChars,
    ReleaseStringUTFChars,

    GetArrayLength,

    NewObjectArray,
    GetObjectArrayElement,
    SetObjectArrayElement,

    NewBooleanArray,
    NewByteArray,
    NewCharArray,
    NewShortArray,
    NewIntArray,
    NewLongArray,
    NewFloatArray,
    NewDoubleArray,

    GetBooleanArrayElements,
    GetByteArrayElements,
    GetCharArrayElements,
    GetShortArrayElements,
    GetIntArrayElements,
    GetLongArrayElements,
    GetFloatArrayElements,
    GetDoubleArrayElements,

    ReleaseBooleanArrayElements,
    ReleaseByteArrayElements,
    ReleaseCharArrayElements,
    ReleaseShortArrayElements,
    ReleaseIntArrayElements,
    ReleaseLongArrayElements,
    ReleaseFloatArrayElements,
    ReleaseDoubleArrayElements,

    GetBooleanArrayRegion,
    GetByteArrayRegion,
    GetCharArrayRegion,
    GetShortArrayRegion,
    GetIntArrayRegion,
    GetLongArrayRegion,
    GetFloatArrayRegion,
    GetDoubleArrayRegion,
    SetBooleanArrayRegion,
    SetByteArrayRegion,
    SetCharArrayRegion,
    SetShortArrayRegion,
    SetIntArrayRegion,
    SetLongArrayRegion,
    SetFloatArrayRegion,
    SetDoubleArrayRegion,

    RegisterNatives,
    UnregisterNatives,

    MonitorEnter,
    MonitorExit,

    GetJavaVM,

    GetStringRegion,
    GetStringUTFRegion,

    GetPrimitiveArrayCritical,
    ReleasePrimitiveArrayCritical,

    GetStringCritical,
    ReleaseStringCritical,

    NewWeakGlobalRef,
    DeleteWeakGlobalRef,

    ExceptionCheck,

    NewDirectByteBuffer,
    GetDirectBufferAddress,
    GetDirectBufferCapacity,

    GetObjectRefType
  };
View Code

請注意,函式表可以在所有JNI介面指標之間共享。

 二、JNI 介面基本方法(JNI Base Api)

1. GetVersion (獲取版本資訊)

jint GetVersion(JNIEnv *env);

返回JNI的版本號。

引數:

env: jni介面指標  

返回值:

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

在 JDK/JRE 1.1中返回 0x00010001

在 JDK/JRE 1.2中返回 0x00010002

在 JDK/JRE 1.4中返回 0x00010004

在 JDK/JRE 1.6中返回 0x00010006

常量:

SINCE JDK/JRE 1.2:

#define JNI_VERSION_1_1 0x00010001 
#define JNI_VERSION_1_2 0x00010002 
/* Error codes */ 
#define JNI_EDETACHED (-2) /* thread detached from the VM */ 
#define JNI_EVERSION (-3) /* JNI version error 

SINCE JDK/JRE 1.4:

#define JNI_VERSION_1_4 0x00010004

SINCE JDK/JRE 1.6:

#define JNI_VERSION_1_6 0x00010006 

使用例項:

jint version = env->GetVersion();  

2. FindClass(發現Java類)

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

在JDK 1.1發行版中,這個函式載入本地定義的類(locally-defined class), 它搜尋在由環境變數 CLASSPATH 目錄下的子目錄和zip檔案中搜索指定的類名。

從JDK 1.2發行版之後,Java安全模型允許非本地系統類也可以載入和呼叫本地方法。FindClass 函式會使用與當前本地方法關聯的ClassLoader, 並用它來載入本地方法指定的class。如果本地方法屬於系統類(system class),則沒有ClassLoader會被呼叫。否則,將使用正確的ClassLoader來載入(load)和連結(link)指定名稱的類。

從JDK 1.2發行版之後,當通過 Invocation 介面來呼叫 FindClass 函式,將沒有當前的本地方法或與之關聯的ClassLoader。這種情況下,會使用 ClassLoader.getSystemClassLoader 來替代。這個ClassLoader 是虛擬機器用來建立應用(applications)的,它有能力定位到 java.class.path 引數下的所有類。

第二個 name 引數,使用全稱類名或陣列型別簽名(array type signature)。例如,String類的全稱類名為:

"java/lang/String" 

而Object陣列型別的使用:

"[Ljava/lang/Object;" 

注意:一定要注意分隔符不是 . 而是 / ,不要寫錯了。

引數:

env:JNI介面指標 
name:全稱的類名(包名以 / 作為分隔符, 然後緊跟著類名),如果名字以 [開頭(陣列簽名識別符號),則返回一個數組的類,這個字串也是MUTF-8。 

返回值:

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

丟擲異常:

ClassFormatError :如果class內容不是一個有效的class檔案。 

ClassCircularityError:如果class或interface是它自己的父類或父介面,造成迴圈層級關係。 

OutOfMemoryError:如果系統在載入的過程中記憶體不足。

NoClassDefFoundError:如果指定的類或介面沒有被找到。(當name傳null或超長時也會丟擲這個異常) 

使用例項:

// Start thread which receives commands from the SA. 
jclass threadClass = env->FindClass("java/lang/Thread"); 
if (threadClass == NULL) stop("Unable to find class java/lang/Thread"); 
jstring threadName = env->NewStringUTF("Serviceability Agent Command Thread"); 
if (threadName == NULL) stop("Unable to allocate debug thread name"); 
jmethodID ctor = env->GetMethodID(threadClass, "<init>", "(Ljava/lang/String;)V"); 
if (ctor == NULL) stop("Unable to find appropriate constructor for java/lang/Thread"); 
// Allocate thread object 
jthread thr = (jthread) env->NewObject(threadClass, ctor, threadName);  

以上程式碼來之 openJDK 原始碼 中的 /hotspot/agent/src/share/native/jvmdi/sa.cpp

 3. GetSuperclass (獲取父類)

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 .

使用例項:

jclass clazz = env->GetObjectClass(thiz); 
clazz = env->GetSuperclass(clazz); 
jfieldID __state = env->GetFieldID(clazz, "__state", "J");  

以上程式碼來至:https://github.com/liucheng98/mesos-0.22.0/blob/5cfd2c36c0e8361fa2e2d9b6f191a738d22a9cfd/src/java/jni/org_apache_mesos_state_LogState.cpp

4. IsAssignableFrom (檢查類是否能轉型)

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(介面)的實現類。(也屬於向上轉型) 

使用例項:

jclass listInterface = env->FindClass("java/util/List") 
jclass arrayListClass = env->FindClass("java/util/ArrayList") 
jboolean isSafe = env->IsAssignableFrom(arrayListClass, listInterface); 
// isSafe: true; 
isSafe = env->IsAssignableFrom(listInterface, arrayListClass); 
// isSafe: false; 

三、 JNI 異常方法 (JNI Exception) 

1. Throw (丟擲異常)

jint Throw(JNIEnv *env, jthrowable obj);  

觸發一個 java.lang.Throwable 物件的異常被丟擲。

引數:

env:JNI介面指標 
obj: java.lang.Throwable 物件 

返回值:

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

丟擲異常:

丟擲 java.lang.Throwable 物件 

使用例項:

jthrowable exception = env->ExceptionOccurred(); 
if (exception) { 
env->ExceptionClear(); 
detach_internal(env, this_obj); 
env->Throw(exception); 
return; 
}  

以上程式碼來至 OpenJDK 原始碼 /hotspot/agent/src/os/solaris/proc/saproc.cpp

2. ThrowNew (構造一個異常物件並丟擲)

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

Exception物件的構造器函式,message為異常的錯誤訊息,clazz為異常的類。

引數:

env:JNI介面指標 
clazz: java.lang.Throwable 的子類 
message: 用於建立 java.lang.Throwable 物件時傳入的錯誤訊息。這個是字串是MUTF-8編碼。 

返回值: 

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

丟擲異常: 

丟擲剛構造出來的 java.lang.Throwable 物件 

使用例項:

env->ThrowNew(env->FindClass("sun/jvm/hotspot/debugger/DebuggerException"), errMsg); 

3. ExceptionOccurred (檢查是否有異常被丟擲)

jthrowable ExceptionOccurred(JNIEnv *env); 

檢查是否有異常被丟擲。這個異常在本地方法呼叫 ExceptionClear() 方法或被Java程式碼處理這個異常之前都會保持在被丟擲狀態。

引數:

env:JNI介面指標 

返回值:

返回過程中丟擲的異常,或沒有異常被丟擲時返回 NULL 。

使用例項:

jthrowable exception = env->ExceptionOccurred(); 
if (exception) { 
env->ExceptionClear(); 
detach_internal(env, this_obj); 
env->Throw(exception); 
return; 
} 

以上程式碼來至 OpenJDK 原始碼 /hotspot/agent/src/os/solaris/proc/saproc.cpp

4. ​ExceptionDescribe (列印異常的stack trace)

void ExceptionDescribe(JNIEnv *env);  

列印一個異常的stack trace到系統的錯誤輸出,例如 stderr 這是為了除錯提供便利。

引數:

env:JNI介面指標 

使用例項:

jthrowable exc = safe_ExceptionOccurred(env); 
if (exc) { 
env->DeleteLocalRef(exc); 
env->ExceptionDescribe(); 
env->ExceptionClear(); 
}  

以上程式碼來至:OpenJDK 原始碼中的 /jdk/src/windows/native/sun/windows/awt_Object.cpp

5. ExceptionClear (清理所有即將丟擲的異常)

void ExceptionClear(JNIEnv *env); 

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

引數:

env:JNI介面指標 

使用例項:

jthrowable exc = safe_ExceptionOccurred(env); 
if (exc) { 
env->DeleteLocalRef(exc); 
env->ExceptionDescribe(); 
env->ExceptionClear(); 
}  

以上程式碼來至:OpenJDK 原始碼中的 /jdk/src/windows/native/sun/windows/awt_Object.cpp

6. FatalError (丟擲一個嚴重的錯誤)

void FatalError(JNIEnv *env, const char *msg); 

丟擲一個嚴重錯誤,並不希望虛擬機器恢復。 

引數:

env:JNI介面指標 
msg : 錯誤訊息,這個字串為MUTF-8編碼 

使用例項:

env->CallVoidMethod(currentThread, setThreadName, threadName) ; 
if( env->ExceptionCheck() ) { 
env->ExceptionDescribe() ; 
env->FatalError("setting thread name failed: could not start reading thread"); 
return 0L ; 
}  

以上程式碼來至:https://github.com/tnarnold/netsnmpj/blob/3153c3e9297dd7de7db9d1a65c97f77937ae147b/netsnmpj-preliminary/native/nativeThread.cc

7. ExceptionCheck (快速檢查是否有異常被丟擲)

jboolean ExceptionCheck(JNIEnv *env); 

這是一個快速函式用於檢查是否有被丟擲的異常,而不建立一個這個異常的區域性引用。

引數:

env:JNI介面指標 

返回值: 

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

使用例項:

env->CallVoidMethod(thread, runId); 
if (env->ExceptionCheck()) { 
env->ExceptionDescribe(); 
env->ExceptionClear(); 
// handle exception 
}  

以上程式碼來至:OpenJDK 原始碼中的 /jdk/src/windows/native/sun/windows/awt_Toolkit.cpp