目錄
第一章 介紹
第二章 設計機制
第三章 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
引用)。
為了克服這個局限性,建議的方法是使用 NewLocalRef
或 NewGlobalRef
函數來創建一個標準的(強)局部或全局引用來指向同一個對象(同弱全局引用指向的那個對象),這些函數會在對象被釋放后返回 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);
}
?
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>Field
和 Set<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)
?