日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JNI实现源码分析【四 函数调用】

發布時間:2025/3/15 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JNI实现源码分析【四 函数调用】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

有了前面的鋪墊,終于可以說說虛擬機是如何調用JNI方法的了。JNI方法,對應Java中的native方法,所以我們跟蹤對Native方法的處理即可。

在徹底弄懂dalvik字節碼【一】中,我們跟蹤過非Native方法的調用,現在我們來跟蹤Native方法的調用,從dvmCallMethodV入手吧:

0x01:dvmCallMethodV

void dvmCallMethodV(Thread* self, const Method* method, Object* obj,bool fromJni, JValue* pResult, va_list args) {...if (dvmIsNativeMethod(method)) {TRACE_METHOD_ENTER(self, method);/** Because we leave no space for local variables, "curFrame" points* directly at the method arguments.*/(*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult,method, self);TRACE_METHOD_EXIT(self, method);} else {dvmInterpret(self, method, pResult);}... }

可以看到,當發現是Native方法時,直接調用Method.nativeFunc

0x02:nativeFunc

看看Method中的定義:

DalvikBridgeFunc nativeFunc;typedef void (*DalvikBridgeFunc)(const u4* args, JValue* pResult,const Method* method, struct Thread* self);

原來是個函數指針。名字既然叫做Bridge,說明是橋接的作用,即主要起到方法的串聯和參數的適配作用。

0x03: 何時賦值

那么這個函數指針何時被賦值了呢?
有好幾處。

a. dvmResolveNativeMethod
在Class.cpp的loadMethodFromDex中,我們看到:

if (dvmIsNativeMethod(meth)) {meth->nativeFunc = dvmResolveNativeMethod;meth->jniArgInfo = computeJniArgInfo(&meth->prototype);}

loadMethodFromDex在從dex中加載類的時候會被調用,也就是說,這里是最初的調用。
所以就是場景就是:我們需要使用到Dex中的一個類,這個類第一次被加載,構建這個類的方法時,發現是一個native方法,將nativeFunc設置成為dvmResolveNativeMethod。

看看dvmResolveNativeMethod做了啥:

void dvmResolveNativeMethod(const u4* args, JValue* pResult,const Method* method, Thread* self) {ClassObject* clazz = method->clazz;/** If this is a static method, it could be called before the class* has been initialized.*/if (dvmIsStaticMethod(method)) {if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {assert(dvmCheckException(dvmThreadSelf()));return;}} else {assert(dvmIsClassInitialized(clazz) ||dvmIsClassInitializing(clazz));}/* start with our internal-native methods */DalvikNativeFunc infunc = dvmLookupInternalNativeMethod(method);if (infunc != NULL) {/* resolution always gets the same answer, so no race here */IF_LOGVV() {char* desc = dexProtoCopyMethodDescriptor(&method->prototype);LOGVV("+++ resolved native %s.%s %s, invoking",clazz->descriptor, method->name, desc);free(desc);}if (dvmIsSynchronizedMethod(method)) {ALOGE("ERROR: internal-native can't be declared 'synchronized'");ALOGE("Failing on %s.%s", method->clazz->descriptor, method->name);dvmAbort(); // harsh, but this is VM-internal problem}DalvikBridgeFunc dfunc = (DalvikBridgeFunc) infunc;dvmSetNativeFunc((Method*) method, dfunc, NULL);dfunc(args, pResult, method, self);return;}/* now scan any DLLs we have loaded for JNI signatures */void* func = lookupSharedLibMethod(method);if (func != NULL) {/* found it, point it at the JNI bridge and then call it */dvmUseJNIBridge((Method*) method, func);(*method->nativeFunc)(args, pResult, method, self);return;}IF_ALOGW() {char* desc = dexProtoCopyMethodDescriptor(&method->prototype);ALOGW("No implementation found for native %s.%s:%s",clazz->descriptor, method->name, desc);free(desc);}dvmThrowUnsatisfiedLinkError("Native method not found", method); }

其中dvmLookupInternalNativeMethod是查找這個方法是不是屬于虛擬機里面定義的Native方法,如果是,則直接調用調用。我們自己寫的native方法自然不是這里。

再看lookupSharedLibMethod,從classpath下的so中查找對應的函數,函數名稱使用了如下格式:

alling dlsym(Java_com_sina_weibo_sdk_net_HttpManager_calcOauthSignNative) calling dlsym(Java_com_sina_weibo_sdk_net_HttpManager_calcOauthSignNative__Landroid_content_Context_2Ljava_lang_String_2Ljava_lang_String_2)

函數名稱構建的代碼:

...mangleCM = mangleString(preMangleCM, len);if (mangleCM == NULL)goto bail;ALOGV("+++ calling dlsym(%s)", mangleCM);func = dlsym(pLib->handle, mangleCM);if (func == NULL) {mangleSig =createMangledSignature(&meth->prototype);if (mangleSig == NULL)goto bail;mangleCMSig = (char*) malloc(strlen(mangleCM) + strlen(mangleSig) +3);if (mangleCMSig == NULL)goto bail;sprintf(mangleCMSig, "%s__%s", mangleCM, mangleSig);ALOGV("+++ calling dlsym(%s)", mangleCMSig);func = dlsym(pLib->handle, mangleCMSig);if (func != NULL) {ALOGV("Found '%s' with dlsym", mangleCMSig);}} else {ALOGV("Found '%s' with dlsym", mangleCM);}

所以,這里我們看到了,默認的函數名映射的規則是:Java_you_pakcage_ClassName_MethodName[__methoidSig],當通過Java_you_pakcage_ClassName_MethodName找不到時,會再嘗試Java_you_pakcage_ClassName_MethodName__methoidSig來查找。

在找到c函數后,通過dvmUseJNIBridge來建立聯系:

void dvmUseJNIBridge(Method* method, void* func) {method->shouldTrace = shouldTrace(method);// Does the method take any reference arguments?method->noRef = true;const char* cp = method->shorty;while (*++cp != '\0') { // Pre-increment to skip return type.if (*cp == 'L') {method->noRef = false;break;}}DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod;dvmSetNativeFunc(method, bridge, (const u2*) func); }

在正常情況下,gDvmJni.useCheckJni為false,所以bridge函數為dvmCallJNIMethod:

void dvmCallJNIMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) {u4* modArgs = (u4*) args;jclass staticMethodClass = NULL;u4 accessFlags = method->accessFlags;bool isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0;//ALOGI("JNI calling %p (%s.%s:%s):", method->insns,// method->clazz->descriptor, method->name, method->shorty);/** Walk the argument list, creating local references for appropriate* arguments.*/int idx = 0;Object* lockObj;if ((accessFlags & ACC_STATIC) != 0) {lockObj = (Object*) method->clazz;/* add the class object we pass in */staticMethodClass = (jclass) addLocalReference(self, (Object*) method->clazz);} else {lockObj = (Object*) args[0];/* add "this" */modArgs[idx++] = (u4) addLocalReference(self, (Object*) modArgs[0]);}if (!method->noRef) {const char* shorty = &method->shorty[1]; /* skip return type */while (*shorty != '\0') {switch (*shorty++) {case 'L'://ALOGI(" local %d: 0x%08x", idx, modArgs[idx]);if (modArgs[idx] != 0) {modArgs[idx] = (u4) addLocalReference(self, (Object*) modArgs[idx]);}break;case 'D':case 'J':idx++;break;default:/* Z B C S I -- do nothing */break;}idx++;}}if (UNLIKELY(method->shouldTrace)) {logNativeMethodEntry(method, args);}if (UNLIKELY(isSynchronized)) {dvmLockObject(self, lockObj);}ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_NATIVE);ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */assert(method->insns != NULL);JNIEnv* env = self->jniEnv;COMPUTE_STACK_SUM(self);dvmPlatformInvoke(env,(ClassObject*) staticMethodClass,method->jniArgInfo, method->insSize, modArgs, method->shorty,(void*) method->insns, pResult);CHECK_STACK_SUM(self);dvmChangeStatus(self, oldStatus);convertReferenceResult(env, pResult, method, self);if (UNLIKELY(isSynchronized)) {dvmUnlockObject(self, lockObj);}if (UNLIKELY(method->shouldTrace)) {logNativeMethodExit(method, self, *pResult);} }

這個函數重點說一下,做了幾個事情:

  • 判斷是否是靜態方法,如果是,就將ClassObject的間接引用設置為第一個參數。如果不是,則將this對象的間接引用設置為第一個參數。這就和JNI方法中的參數定義對應起來了: Java_you_package_Class_StaticMethod(JNIEnv *env, jclass type, ...) {} Java_you_package_Class_Method(JNIEnv *env, jobject jthis, ...) {}
  • 判斷參數中是否存在引用類型的對象(非原型對象),如果有,將對象添加到局部引用表中獲取其間接引用,替換參數。
  • 調用dvmPlatformInvoke,最終就會調用到JNI方法了。dvmPlatformInvoke對不同的ABI有不同的實現。
  • 從pResult中獲取返回值,如果是間接引用,則轉化為真實的對象。
  • b. RegisterNatives
    另外一種是通過自行調用JNIEnv.RegisterNatives來完成注冊:

    static jint RegisterNatives(JNIEnv* env, jclass jclazz,const JNINativeMethod* methods, jint nMethods) {ScopedJniThreadState ts(env);ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);if (gDvm.verboseJni) {ALOGI("[Registering JNI native methods for class %s]",clazz->descriptor);}for (int i = 0; i < nMethods; i++) {if (!dvmRegisterJNIMethod(clazz, methods[i].name,methods[i].signature, methods[i].fnPtr)){return JNI_ERR;}}return JNI_OK; }

    主要是dvmRegisterJNIMethod:

    static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,const char* signature, void* fnPtr) {...dvmUseJNIBridge(method, fnPtr);... }

    又是dvmUseJNIBridge,剩下的就和前面一樣了。
    所以主動注冊與默認查找的區別就是,主動注冊需要告訴JNI,Java方法和C函數的映射,而默認查找則按照對應的規則去查找。對后在調用邏輯上,完全一致。

    同時,我們也看到了,在調用C函數前,真實的對象被轉化為間接引用,然后傳遞到JNI方法中,同時,JNI方法返回的間接引用被轉化為真實的對象,供下一步使用。



    作者:difcareer
    鏈接:http://www.jianshu.com/p/1ef556aec1cd
    來源:簡書
    著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。

    總結

    以上是生活随笔為你收集整理的JNI实现源码分析【四 函数调用】的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。