目錄
第一章 介紹
第二章 設(shè)計機(jī)制
第三章 JNI類型和數(shù)據(jù)結(jié)構(gòu)
第四章 JNI函數(shù)(1)
第四章 JNI函數(shù)(2)
第四章 JNI函數(shù)(3)
第四章 JNI函數(shù)(4)
第五章 Invocation API
第四章 JNI函數(shù)
4.15 操作監(jiān)視器(同步鎖)
MonitorEnter
jint MonitorEnter(JNIEnv *env, jobject obj);
進(jìn)入一個obj的監(jiān)視區(qū)monitor。
即使用 obj 作為鎖對象(可能是對象鎖,也可能是類鎖),obj必須不能為 null。
每個java對象內(nèi)部都有一個鎖(monitor),如果當(dāng)前線程已經(jīng)擁有了obj的monitor,它會在其監(jiān)視區(qū)增加計數(shù)器,記錄線程進(jìn)入監(jiān)視區(qū)的次數(shù)。如果obj上的監(jiān)視區(qū)沒有被任何其他線程持有,則當(dāng)前線程開始持有監(jiān)視區(qū)的鎖,設(shè)置監(jiān)視區(qū)的計數(shù)器為1;如果其他線程已經(jīng)擁有了這個監(jiān)視區(qū)的鎖,則當(dāng)前線程一直等待鎖被釋放,然后再嘗試獲取鎖。
通過 MonitorEnter 這個JNI函數(shù)進(jìn)入到一個監(jiān)視區(qū)中,是無法使用Java虛擬機(jī) monitorexit 指令或一個同步方法返回來退出這個監(jiān)視區(qū)的。MonitorEnter函數(shù)和Java虛擬機(jī) monitorexit 指令之間可能搶著進(jìn)入同一個obj的監(jiān)視區(qū)。
為了避免死鎖,使用 MonitorEnter方法來進(jìn)入監(jiān)視區(qū),必須使用MonitorExit 來退出,或調(diào)用 DetachCurrentThread 來明確釋放JNI監(jiān)視區(qū)。
參數(shù):
-
env:JNI接口指針 -
obj:一個普通的java對象或class對象。
返回值:
成功返回0,失敗返回負(fù)數(shù)。
使用實例:
env->MonitorEnter(mMonitor);
env->CallVoidMethod(mMonitor, mWaitMethod);
env->MonitorExit(mMonitor);
?
MonitorExit
jint MonitorExit(JNIEnv *env, jobject obj);
當(dāng)前線程必須持有obj的monitor,當(dāng)前線程進(jìn)入monitor的計數(shù)器減1,如果計數(shù)器的值變?yōu)榱?,則當(dāng)前線程釋放monitor。
本地代碼不能使用 MonitorExit 函數(shù)來退出一個通過Java虛擬機(jī) monitorexit指令或一個同步方法進(jìn)入的monitor。
參數(shù):
-
env:JNI接口指針 -
obj:一個java對象或一個class對象
返回值:
成功返回0,失敗返回負(fù)數(shù)。
排除異常:
-
IllegalMonitorStateException如果當(dāng)前線程還沒持有monitor。
?
?
4.16 NIO支持
NewDirectByteBuffer
jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity);
分配并返回一個 java.nio.ByteBuffer ,指向一塊內(nèi)存,地址開始于 address ,并有 capacity 個bytes的內(nèi)存空間。
本地代碼調(diào)用這個函數(shù)并生成byte-buffer對象給一個Java層,必須確保這個buffer指向一個有效的可讀內(nèi)存空間,如果需要,也得是可寫的內(nèi)存。如果從Java代碼嘗試訪問一個無效的內(nèi)存空間,可能返回一個隨機(jī)的值,或沒有任何可見反應(yīng),或拋出一個未指定的異常。
JDK/JRE 1.4及以上支持該函數(shù)。
參數(shù):
-
env:JNI接口指針 -
address:內(nèi)存空間起始地址(必須不為NULL) -
capacity:內(nèi)存空間的大小(字節(jié)數(shù))(必須為整數(shù))
返回值:
返回剛創(chuàng)建的 java.nio.ByteBuffer 對象的局部引用。如果有異常拋出或者虛擬機(jī)不允許JNI訪問直接的buffer,則返回 NULL
拋出異常:
-
OutOfMemoryError: 如果分配ByteBuffer對象失敗時。
使用實例:
jobject pDataBuf = null;
if (filep->data[0].len > 0)
pDataBuf = env->NewDirectByteBuffer(filep->data[0].ptr,
filep->data[0].len);
env->SetObjectArrayElement(pParts, pidx++, pDataBuf);
pDataBuf = null;
if (filep->data[1].len > 0)
pDataBuf = env->NewDirectByteBuffer(filep->data[1].ptr,
filep->data[1].len);
env->SetObjectArrayElement(pParts, pidx++, pDataBuf);
以上代碼來至:Jni.cpp (openjdk\jdk\src\share\native\com\sun\java\util\jar\pack)
?
GetDirectBufferAddress
void* GetDirectBufferAddress(JNIEnv* env, jobject buf);
獲取給定的 java.nio.Buffer 的起始內(nèi)存地址。
這個函數(shù)通過buffer對象允許本地代碼訪問Java代碼可訪問的同一塊內(nèi)存地址。
JDK/JRE 1.4及以上支持該函數(shù)。
參數(shù):
-
env:JNI接口指針 -
buf:給定的java.nio.Buffer對象(必須不能為NULL)
返回值:
返回buffer指向的起始內(nèi)存地址。如果內(nèi)存地址是undefined的,如果給定的jobject不是 java.nio.Buffer 或者如果虛擬機(jī)不支持JNI訪問直接的buffer,則返回 NULL 。
使用實例:
JNIEXPORT jclass JNICALL
Java_java_lang_ClassLoader_defineClass2(JNIEnv *env,
jobject loader,
jstring name,
jobject data,
jint offset,
jint length,
jobject pd,
jstring source)
{
jbyte *body;
char *utfName;
jclass result = 0;
char buf[128];
char* utfSource;
char sourceBuf[1024];
assert(data != NULL); // caller fails if data is null.
assert(length >= 0); // caller passes ByteBuffer.remaining() for length, so never neg.
// caller passes ByteBuffer.position() for offset, and capacity() >= position() + remaining()
assert((*env)->GetDirectBufferCapacity(env, data) >= (offset + length));
body = (*env)->GetDirectBufferAddress(env, data);
if (body == 0) {
JNU_ThrowNullPointerException(env, 0);
return 0;
}
// ...
}
以上代碼來至:ClassLoader.c (openjdk\jdk\src\share\native\java\lang)
?
GetDirectBufferCapacity
jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf);
獲取并返回給定 java.nio.Buffer 對象的內(nèi)存空間大?。╟apacity)。capacity是內(nèi)存空間包含的元素的個數(shù)(the number of elements that the memory region contains)
參數(shù):
-
env:JNI接口指針 -
buf: 給定的buffer對象。(必須不能為NULL)
返回值:
返回buffer對應(yīng)的內(nèi)存空間的capacity。如果給定的jobject不是 java.nio.Buffer ,或者the object is an unaligned view buffer and the processor architecture does not support unaligned access,或者虛擬機(jī)不允許JNI訪問direct buffer,則返回 -1 。
使用實例:
請參見上一個函數(shù)的例子。
?
?
4.17 反射支持
開發(fā)者如果知道方法或域的名稱和類型,可以使用JNI來調(diào)用Java方法或訪問Java域。Java反射API提供運(yùn)行時自省。JNI提供一組函數(shù)來使用Java反射API。
?
FromReflectedMethod
jmethodID FromReflectedMethod(JNIEnv *env, jobject method);
通過 java.lang.reflect.Method 或 java.lang.reflect.Constructor 來獲取其反射的目標(biāo)方法對應(yīng)的 methodId.
參數(shù):
-
env:JNI接口指針 -
jobject:一個java.lang.reflect.Method或java.lang.reflect.Constructor對象。
返回值:
反射的目標(biāo)方法的methodID
使用實例:
extern void __attribute__ ((visibility ("hidden"))) dalvik_replaceMethod(
JNIEnv* env, jobject src, jobject dest) {
jobject clazz = env->CallObjectMethod(dest, jClassMethod);
ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr(
dvmThreadSelf_fnPtr(), clazz);
clz->status = CLASS_INITIALIZED;
Method* meth = (Method*) env->FromReflectedMethod(src);
Method* target = (Method*) env->FromReflectedMethod(dest);
LOGD("dalvikMethod: %s", meth->name);
// meth->clazz = target->clazz;
meth->accessFlags |= ACC_PUBLIC;
meth->methodIndex = target->methodIndex;
meth->jniArgInfo = target->jniArgInfo;
meth->registersSize = target->registersSize;
meth->outsSize = target->outsSize;
meth->insSize = target->insSize;
meth->prototype = target->prototype;
meth->insns = target->insns;
meth->nativeFunc = target->nativeFunc;
}
以下是個人理解部分:
這里講一個題外話,上面的例子是來至阿里巴巴開源的Android熱修復(fù)框架andFix。其中就使用了 FromReflectedMethod 來實現(xiàn)替換有BUG的方法,以實現(xiàn)熱修復(fù)的目標(biāo)。
它的實現(xiàn)原理是通過將目標(biāo)方法修改為native方法,然后修改其 nativeFunc 的指針來實現(xiàn)替換一個方法。
?
FromReflectedField
jfieldID FromReflectedField(JNIEnv *env, jobject field);
通過 java.lang.reflect.Field 對象來獲取fieldID.
參數(shù):
-
env:JNI接口指針 -
field:java.lang.reflect.Field對象
返回值:
目標(biāo)成員域的fieldID
使用實例:
JNIEXPORT void JNICALL Java_brian_com_nativehotfixdemo_hotfix_HotfixManager_setSingleFlagArt
(JNIEnv* env, jobject obj, jobject field) {
Field* dalvikField = (Field*) env->FromReflectedField(field);
dalvikField->accessFlags = dalvikField->accessFlags & (~ACC_PRIVATE) | ACC_PUBLIC;
}
?
ToReflectedMethod
jobject ToReflectedMethod(JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);
轉(zhuǎn)換 cls 的 methodID 為 java.lang.reflect.Method 或java.lang.reflect.Constructor對象。
參數(shù):
-
env:JNI接口指針 -
cls:目標(biāo)方法的java類對象 -
jmethodId:目標(biāo)方法的methodID -
isStatic:目標(biāo)方法為靜態(tài)的則比較將isStatic設(shè)置為JNI_TRUE,否則設(shè)置為JNI_FALSE。
返回值:
java.lang.reflect.Method 或java.lang.reflect.Constructor對象
使用實例:
extern "C" JNIEXPORT jobject JNICALL Java_JniTest_testGetMirandaMethodNative(JNIEnv* env, jclass) {
jclass abstract_class = env->FindClass("JniTest$testGetMirandaMethod_MirandaAbstract");
assert(abstract_class != NULL);
jmethodID miranda_method = env->GetMethodID(abstract_class, "inInterface", "()Z");
assert(miranda_method != NULL);
return env->ToReflectedMethod(abstract_class, miranda_method, JNI_FALSE);
}
?
ToReflectedField
jobject ToReflectedField(JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic);
轉(zhuǎn)換 cls 的 fieldID 為 java.lang.reflect.Field 對象。
參數(shù):
-
env:JNI接口指針 -
cls:目標(biāo)方法的java類對象 -
jmethodId:目標(biāo)方法的methodID -
isStatic:目標(biāo)方法為靜態(tài)的則比較將isStatic設(shè)置為JNI_TRUE,否則設(shè)置為JNI_FALSE。
返回值:
java.lang.reflect.Field 對象
?
?
4.18 Java虛擬機(jī)接口
GetJavaVM
jint GetJavaVM(JNIEnv *env, JavaVM **vm);
返回關(guān)聯(lián)到當(dāng)前線程的Java虛擬機(jī)接口指針。
參數(shù):
-
env:JNI接口指針 -
vm: 用于存放返回的Java虛擬機(jī)指針
返回值:
成功返回0,失敗返回負(fù)數(shù)。
使用實例:
if (JNI_OnLoad) {
JavaVM *jvm;
(*env)->GetJavaVM(env, &jvm);
jniVersion = (*JNI_OnLoad)(jvm, NULL);
} else {
jniVersion = 0x00010001;
}
以上代碼來至:OpenJDK/jdk/src/share/native/java/lang/ClassLoader.c
?