<!– @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代碼進入虛擬機運行瞭。