Android——Dalvik虛擬機運行ZygoteInit類 – Android移動開發技術文章_手機開發 Android移動開發教學課程

<!– @page { margin: 2cm } P { margin-bottom: 0.21cm } –>


從上一節可以知道Dalvik虛擬機入口點和創建虛擬機的函數,這一節繼續分析運行時類調用虛擬機的代碼片段,需要搞清楚怎麼樣運行JAVA的ZygoteInit類,Dalvik虛擬機又提供什麼樣的接口調用。運行時類代碼如下:


/* start the virtual machine */


if (startVm(&mJavaVM, &env) != 0)


goto bail;


這一段是創建虛擬機,並準備好所有運行dex代碼的環境。


 


/*


* Register android functions.


*/


if (startReg(env) < 0) {


LOGE(“Unable to register all android natives
“);


goto bail;


}


這一段是註冊所有android提供的本地方法,所謂的本地方法,其實是相對於JAVA裡定義的方法,比如像C或C++等提供二進制運行的方法。


 


 


/*


* We want to call main() with a String array with arguments in it.


* At present we only have one argument, the class name. Create an


* array to hold it.


*/


jclass stringClass;


jobjectArray strArray;


jstring classNameStr;


jstring startSystemServerStr;


 


stringClass = env->FindClass(“java/lang/String”);


assert(stringClass != NULL);


strArray = env->NewObjectArray(2, stringClass, NULL);


assert(strArray != NULL);


classNameStr = env->NewStringUTF(className);


assert(classNameStr != NULL);


env->SetObjectArrayElement(strArray, 0, classNameStr);


startSystemServerStr = env->NewStringUTF(startSystemServer ?


“true” : “false”);


env->SetObjectArrayElement(strArray, 1, startSystemServerStr);


這一段代碼主要作用是把類型參數className轉換為虛擬機裡調用方法的參數,就是轉為 strArray數組表示,同時可以添加多個參數。


 


 


/*


* Start VM. This thread becomes the main thread of the VM, and will


* not return until the VM exits.


*/


jclass startClass;


jmethodID startMeth;


 


slashClassName = strdup(className);


這一行代碼是拷貝類名稱。


 


for (cp = slashClassName; *cp != ; cp++)


if (*cp == .)


*cp = /;


這一段代碼是把類名稱 com.android.internal.os.ZygoteInit轉換為 com/android/internal/os/ZygoteInit,為什麼要轉換這樣的方式呢?其實仔細思考一下,發現這不正是linux下的目錄表示方式嗎?是的,就是把類的點連接符變換為目錄方式,這要就可以到相應的目錄裡找到執行的代碼文件。


 


LOGD(“caijs add: JavaVM load class %s
“, slashClassName);


startClass = env->FindClass(slashClassName);


if (startClass == NULL) {


LOGE(“JavaVM unable to locate class %s
“, slashClassName);


/* keep going */


} else {


這一段代碼主要通過類目錄結構com/android/internal/os/ZygoteInit,查找到類的代碼,查找到瞭就保存在startClass變量裡。到這裡,已經接觸到Dalvik虛擬機提供瞭最重要的一個方法,它就是 FindClass方法接口,這個接口比較強大,隻要提供類的目錄結構,就可以找到相應的執行代碼,這樣就可以找類相關的方法入口,才可以給虛擬機解釋器執行。因此,後面要好好瞭解和分析這個接口的實現。


 


 


startMeth = env->GetStaticMethodID(startClass, “main”,


“([Ljava/lang/String;)V”);


if (startMeth == NULL) {


LOGE(“JavaVM unable to find main() in %s
“, className);


/* keep going */


} else {


這一段代碼是查找方法入口點的ID。主要就是在前面找到的類代碼基礎之上,然後通過方法名稱“main”調用GetStaticMethodID接口,查找到方法的ID。這個方法ID是給後面虛擬機運行這個方法使用的,因為一個類裡有很多方法,每個方法都有一個ID,隻有通過這個ID才可以找到相應的方法來運行。在這段代碼裡,有一個特別的地方,就是 GetStaticMethodID方法最後一個參數“([Ljava/lang/String;)V”。這個參數是一個字符串,但內容排列比較奇怪,其實它是一種對函數返回值和參數的編碼。這種編碼叫做JNI字段描述符(Java Native Interface Field Descriptors)。


 


 


LOGD(“caijs add: JavaVM find main() in %s
“, className);


env->CallStaticVoidMethod(startClass, startMeth, strArray);


這一行代碼主要調用虛擬機的接口CallStaticVoidMethod 來運行 com.android.internal.os.ZygoteInit類裡的main方法。在Android系統裡,運行這個類代碼之後,就不再返回來瞭,這個進程就變成虛擬機的主進程,這個虛擬機就變成主要運行ZygoteInit類的虛擬機瞭,其它應用程序的虛擬機都是從這個虛擬克隆出來,以達到快速地產生派生的虛擬機,每個應用程序一個虛擬機的健壯性、安全性。


 


#if 0


if (env->ExceptionCheck())


threadExitUncaughtException(env);


#endif


}


}


 


LOGD(“Shutting down VM
“);


if (mJavaVM->DetachCurrentThread() != JNI_OK)


LOGW(“Warning: unable to detach main thread
“);


if (mJavaVM->DestroyJavaVM() != 0)


LOGW(“Warning: VM did not shut down cleanly
“);


這一段代碼是關閉所有虛擬機時調用,基本上不會調用這段代碼的。


 


bail:


free(slashClassName);


這一行代碼是初始化出錯時調用。


 


}


 


在一節裡,學習瞭Davlik虛擬機三大接口函數:FindClass、GetStaticMethodID和 CallStaticVoidMethod。其實理解起來很簡單,就是通過FindClass接口查找到相應的Java類代碼,然後在這個類代碼用GetStaticMethodID接口查找到相應的方法ID,最後通過 CallStaticVoidMethod接口運行相應的方法代碼,就完成Java代碼進入虛擬機運行瞭。


 

You May Also Like