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

从上一节可以知道Dalvik虚拟机入口点和创建虚拟机的函数,这一节继续分析运行时类调用虚拟机的代码片段,需要搞清楚怎么样运行JAVAZygoteInit类,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/n");

goto bail;

}

这一段是注册所有android提供的本地方法,所谓的本地方法,其实是相对于JAVA里定义的方法,比如像CC++等提供二进制运行的方法。

 

 

/*

* 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 != '/0'; cp++)

if (*cp == '.')

*cp = '/';

这一段代码是把类名称 com.android.internal.os.ZygoteInit转换为 comandroidinternalosZygoteInit,为什么要转换这样的方式呢?其实仔细思考一下,发现这不正是linux下的目录表示方式吗?是的,就是把类的点连接符变换为目录方式,这要就可以到相应的目录里找到执行的代码文件。

 

LOGD("caijs add: JavaVM load class '%s'/n", slashClassName);

startClass = env->FindClass(slashClassName);

if (startClass == NULL) {

LOGE("JavaVM unable to locate class '%s'/n", slashClassName);

/* keep going */

} else {

这一段代码主要通过类目录结构comandroidinternalosZygoteInit,查找到类的代码,查找到了就保存在startClass变量里。到这里,已经接触到Dalvik虚拟机提供了最重要的一个方法,它就是 FindClass方法接口,这个接口比较强大,只要提供类的目录结构,就可以找到相应的执行代码,这样就可以找类相关的方法入口,才可以给虚拟机解释器执行。因此,后面要好好了解和分析这个接口的实现。

 

 

startMeth = env->GetStaticMethodID(startClass, "main",

"([Ljava/lang/String;)V");

if (startMeth == NULL) {

LOGE("JavaVM unable to find main() in '%s'/n", 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'/n", 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/n");

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

LOGW("Warning: unable to detach main thread/n");

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

LOGW("Warning: VM did not shut down cleanly/n");

这一段代码是关闭所有虚拟机时调用,基本上不会调用这段代码的。

 

bail:

free(slashClassName);

这一行代码是初始化出错时调用。

 

}

 

在一节里,学习了Davlik虚拟机三大接口函数:FindClassGetStaticMethodIDCallStaticVoidMethod。其实理解起来很简单,就是通过FindClass接口查找到相应的Java类代码,然后在这个类代码用GetStaticMethodID接口查找到相应的方法ID,最后通过 CallStaticVoidMethod接口运行相应的方法代码,就完成Java代码进入虚拟机运行了。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐