JNI技術規范 - 第四章 JNI函數(2)

目錄

第一章 介紹
第二章 設計機制
第三章 JNI類型和數據結構
第四章 JNI函數(1)
第四章 JNI函數(2)
第四章 JNI函數(3)
第四章 JNI函數(4)
第五章 Invocation API

第四章 JNI函數

4.5 全局和局部引用

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

NewGlobalRef

jobject NewGlobalRef(JNIEnv *env, jobject obj);

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

參數:

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

返回值:

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

使用實例:

// Remember which thread this is
debugThreadObj = env->NewGlobalRef(thr);
if (debugThreadObj == NULL) stop("Unable to allocate global ref for debug thread object");

以上代碼來至:OpenJDK 源碼中的 /hotspot/agent/src/share/native/jvmdi/sa.cpp

?

DeleteGlobalRef

void DeleteGlobalRef(JNIEnv *env, jobject globalRef);

刪除指向 gloablRef 的全局變量。

參數:

  • env :JNI接口指針
  • globalRef: 需要刪除的全局引用

返回值:

使用實例:

jthread thr = suspendedThreads[i];
jvmdiError err = jvmdi->ResumeThread(thr);
env->DeleteGlobalRef(thr);

以上代碼來至:OpenJDK 源碼中的 /hotspot/agent/src/share/native/jvmdi/sa.cpp

?

DeleteLocalRef

void DeleteLocalRef(JNIEnv *env, jobject localRef);

刪除 localRef 的局部引用。

參數:

  • env :JNI接口指針
  • localRef:需要刪除的局部引用。

返回值:

使用實例:

for (i = 0; i < itemCount; i++)
{
  jstring item = (jstring)env->GetObjectArrayElement(items, i);
  JNI_CHECK_NULL_GOTO(item, "null item", next_elem);
  c->SendMessage(CB_INSERTSTRING, index + i, JavaStringBuffer(env, item));
  env->DeleteLocalRef(item);
}

以上代碼來至:OpenJDK 源碼中的 /jdk/src/windows/native/sun/windows/awt_Choice.cpp

注意:

在JDK/JRE 1.1的時候提供 DeleteLocalRef 函數,因此開發者可以手動的刪除局部引用。例如,在本地方法遍歷一個大數組的時候,需要在遍歷過程中對每個元素進行處理,在這種情況下正確的做法是在創建一個新的本地引用之前,先把上一個不再被使用的本地引用刪除掉。

而在JDK/JRE 1.2 增加一個一組以下4個函數來提供管理局部引用的生命周期。

?

EnsureLocalCapacity

jint EnsureLocalCapacity(JNIEnv *env, jint capacity);

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

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

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

參數:

  • env :JNI接口指針
  • capacity:

返回值:

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

使用實例:

if (env->EnsureLocalCapacity(1) < 0) {
  return NULL;
}

以上代碼來至:OpenJDK 源碼中的 /jdk/src/windows/native/sun/windows/awt_Canvas.cpp

題外話:虛擬機對于capacity的上限在源碼中定義為常量:

// Pick a reasonable higher bound for local capacity requested
// for EnsureLocalCapacity and PushLocalFrame.  We don't want it too
// high because a test (or very unusual application) may try to allocate
// that many handles and run out of swap space.  An implementation is
// permitted to allocate more handles than the ensured capacity, so this
// value is set high enough to prevent compatibility problems.
const int MAX_REASONABLE_LOCAL_CAPACITY = 4*K;

?

PushLocalFrame

jint PushLocalFrame(JNIEnv *env, jint capacity);

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

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

參數:

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

返回值:

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

使用實例:

    if (env->PushLocalFrame(1) < 0)
        return;

    jobject target = GetTarget(env);

    // To avoid possibly running client code on the toolkit thread, don't
    // do the following checks if we're running on the toolkit thread.
    if (AwtToolkit::MainThread() != ::GetCurrentThreadId()) {
        // Compare number of items.
        int nTargetItems = JNU_CallMethodByName(env, NULL, target,
                                                "countItems", "()I").i;
        DASSERT(!safe_ExceptionOccurred(env));
        int nPeerItems = (int)::SendMessage(GetHWnd(), CB_GETCOUNT, 0, 0);
        DASSERT(nTargetItems == nPeerItems);

        // Compare selection
        int targetIndex = JNU_CallMethodByName(env, NULL, target,
                                               "getSelectedIndex", "()I").i;
        DASSERT(!safe_ExceptionOccurred(env));
        int peerCurSel = (int)::SendMessage(GetHWnd(), CB_GETCURSEL, 0, 0);
        DASSERT(targetIndex == peerCurSel);
    }

    env->PopLocalFrame(0);

以上代碼來至:OpenJDK源碼中的 /jdk/src/windows/native/sun/windows/awt_Choice.cpp

?

PopLocalFrame

jobject PopLocalFrame(JNIEnv *env, jobject result);

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

參數:

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

返回值:

返回給定 result 對象的上一個本地引用棧幀本地引用(a local reference in the previous local reference frame for given result object)

使用實例:

    if (env->PushLocalFrame(2) < 0) {
        return E_OUTOFMEMORY;
    }

    jbyteArray bytes =
        AwtDataTransferer::ConvertData(env, m_component, m_transferable,
                                       (jlong)matchedFormatEtc.cfFormat,
                                       m_formatMap);
    if (!JNU_IsNull(env, safe_ExceptionOccurred(env))) {
        env->ExceptionDescribe();
        env->ExceptionClear();
        env->PopLocalFrame(NULL);
        return E_UNEXPECTED;
    }
    if (bytes == NULL) {
        env->PopLocalFrame(NULL);
        return E_UNEXPECTED;
    }

以上代碼來至:OpenJDK 源碼中的 jdk/src/windows/native/sun/windows/awt_DnDDS.cpp

?

NewLocalRef

jobject NewLocalRef(JNIEnv *env, jobject ref);

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

參數:

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

返回值:

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

使用實例:

    jobject localObj = env->NewLocalRef(jCursor);
    if (localObj != NULL) {
        setPData(localObj, ptr_to_jlong(NULL));
        env->DeleteLocalRef(localObj);
    }
    env->DeleteWeakGlobalRef(jCursor);

以上代碼來至:OpenJDK源碼中的 /jdk/src/windows/native/sun/windows/awt_Cursor.cpp

?

?

4.6 弱全局引用

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

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

澄清(2001年6月新加)

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

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

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

?

NewWeakGlobalRef

jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);

創建一個新的弱全局引用

參數:

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

返回值:

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

使用實例:

    // jCursor是一個弱引用,在使用前先使用 NewLocalRef 創建一個強引用,必須其指向的對象被釋放掉。
    jobject localObj = env->NewLocalRef(jCursor);
    if (localObj != NULL) {
        setPData(localObj, ptr_to_jlong(NULL));
        env->DeleteLocalRef(localObj); // 使用完后,立即釋放強引用,使得弱引用指向的對象可以被釋放掉
    }
    // 刪除弱全局引用
    env->DeleteWeakGlobalRef(jCursor);

以上代碼來至:OpenJDK源碼中的 /jdk/src/windows/native/sun/windows/awt_Cursor.cpp

?

DeleteWeakGlobalRef

void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);

刪除一個弱引用。

參數:

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

返回值:

使用實例:

    // jCursor是一個弱引用,在使用前先使用 NewLocalRef 創建一個強引用,必須其指向的對象被釋放掉。
    jobject localObj = env->NewLocalRef(jCursor);
    if (localObj != NULL) {
        setPData(localObj, ptr_to_jlong(NULL));
        env->DeleteLocalRef(localObj); // 使用完后,立即釋放強引用,使得弱引用指向的對象可以被釋放掉
    }
    // 刪除弱全局引用
    env->DeleteWeakGlobalRef(jCursor);

以上代碼來至:OpenJDK源碼中的 /jdk/src/windows/native/sun/windows/awt_Cursor.cpp

?

4.7 操作對象

AllocObject

jobject AllocObject(JNIEnv *env, jclass clazz);

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

參數:

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

返回值:

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

拋出異常:

  • InstantiationException 如果傳入的 clazz 是一個抽象類或接口。
  • OutOfMemoryError 如果系統沒有內存時。

使用實例:

 // 1、創建一個未初始化的對象,并分配內存
 obj_cat = (*env)->AllocObject(env, cls_cat);
 if (obj_cat) {
    // 2、調用對象的構造函數初始化對象
    (*env)->CallNonvirtualVoidMethod(env, obj_cat, cls_cat, mid_cat_init, c_str_name);
    if ((*env)->ExceptionCheck(env)) { // 檢查異常
        goto quit;
    }
 }

以上代碼來至:http://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/function2.html

?

NewObject, NewObjectA, NewObjectV

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 如果系統沒有內存時。
  • 其他所有構造器可能拋出的異常

使用實例:

     // 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);
    if (thr == NULL) stop("Unable to allocate debug thread's java/lang/Thread instance");

以上代碼來至:OpenJDK源碼中的 /hotspot/agent/src/share/native/jvmdi/sa.cpp

?

GetObjectClass

jclass GetObjectClass(JNIEnv *env, jobject obj);

獲取一個對象的class。

參數:

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

返回值:

一個Java類對象(Java class object)

使用實例:

cls = env->GetObjectClass(self);
fid = env->GetFieldID(cls, "peer", "Ljava/awt/peer/ComponentPeer;");
env->SetObjectField(self, fid, lpeer);

以上代碼來至: OpenJDK 源碼中的 /jdk/src/windows/native/sun/windows/awt_Frame.cpp

?

GetObjectRefType

jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj);

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

參數:

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

返回值:

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

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

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

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

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

使用實例:

jobjectRefType ref = env->GetObjectRefType(stringArray);
if(ref == JNIGlobalRefType) {
  env->DeleteGlobalRef(stringArray);
}

以上代碼來至:https://github.com/wshackle/java4cpp/blob/7f705cd274ab0f42c3ca9cf28242b53c5b33b193/src/main/resources/cpp_easy_method_wrap.cpp

?

IsInstanceOf

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。

使用實例:

    jthrowable xcp = env->ExceptionOccurred();
    if (xcp != NULL) {
        env->ExceptionClear(); // if we don't do this, FindClass will fail

        jclass outofmem = env->FindClass("java/lang/OutOfMemoryError");
        DASSERT(outofmem != NULL);
        jboolean isOutofmem = env->IsInstanceOf(xcp, outofmem);

        env->DeleteLocalRef(outofmem);

        if (isOutofmem) {
            env->DeleteLocalRef(xcp);
            throw std::bad_alloc();
        } else {
            // rethrow exception
            env->Throw(xcp);
            return xcp;
        }
    }

以上代碼來至:OpenJDK 源碼 /jdk/src/windows/native/sun/windows/awt_new.cpp

?

IsSameObject

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

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

參數:

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

返回值:

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

否則返回 JNI_FALSE

使用實例:

// Trying to identify the same FontDescriptor by comparing the
// pointers.
refFontDescriptor = env->GetObjectArrayElement(array, i);
if (env->IsSameObject(refFontDescriptor, fontDescriptor)) {
  env->DeleteLocalRef(refFontDescriptor);
  env->DeleteLocalRef(array);
  return i;
}
env->DeleteLocalRef(refFontDescriptor);

以上代碼來至:OpenJDK 源碼中的 /jdk/src/windows/native/sun/windows/awt_Font.cpp

?

4.8 訪問對象成員域

GetFieldID

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 如果系統內存不足時

使用實例:

cls = env->FindClass("java/awt/Button");
if (cls == NULL) {
  return;
}
AwtButton::labelID = env->GetFieldID(cls, "label", "Ljava/lang/String;");
DASSERT(AwtButton::labelID != NULL);

以上代碼來至:OpenJDK 源碼里的 /jdk/src/windows/native/sun/windows/awt_Button.cpp

?

Get<type>Field

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

返回值:

返回成員域的內容

使用實例:

bufferCapacityField = env->GetFieldID(bufferClass, "capacity", "I");
// ...
ret = env->GetIntField(buf, bufferCapacityField);

以上代碼來至:OpenJDK 源碼 /hotspot/src/share/vm/prims/jni.cpp

?

Set<type>Field

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:設置的新值

返回值:

使用實例:

jint x = env->GetIntField(target, AwtComponent::xID);
jint y = env->GetIntField(target, AwtComponent::yID);
jint width = env->GetIntField(target, AwtComponent::widthID);
jint height = env->GetIntField(target, AwtComponent::heightID);

// ...
env->SetIntField(target, AwtComponent::widthID,  (jint) rc.right);
env->SetIntField(target, AwtComponent::heightID, (jint) rc.bottom);

以上代碼來至:OpenJDK 源碼中的 /jdk/src/windows/native/sun/windows/awt_Chooice.cpp

?

?

4.9 調用對象方法

GetMethodID

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 ; 如果系統內存不足時。

使用實例:

    // 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");

以上代碼來至:OpenJDK 源碼 /hotspot/agent/src/share/native/jvmdi/sa.cpp

?

Call<type>Method(MethodA, MethodV)

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方法時可能拋出的任何異常。

使用實例:

jobject target = w->GetTarget(env);
env->CallVoidMethod(target, AwtFrame::activateEmbeddingTopLevelMID);
env->DeleteLocalRef(target);

以上代碼來至:OpenJDK 源碼里面的 /jdk/src/windows/native/sun/windows/awt_Dialog.cpp

?

CallNonvirtual<type>Method(MethodA, MethodV)

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() 一樣,即在子類里重寫方法里可以調用其父類的方法。

?
第四章 JNI函數(3)
?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,107評論 2 375

推薦閱讀更多精彩內容