您的位置:首页 > 移动开发 > Android开发

Android培训班(56)Dalvik虚拟机的入口点

2011-06-19 18:13 337 查看
<!--
@page { margin: 2cm }
P { margin-bottom: 0.21cm }
-->
要分析Dalvik虚拟机的代码,到底从那里开始比较好呢?从事软件开发人员都知道,每个程序都有生命周期,都有出生点,也就是程序的进入位置。像C语言里控制台程序是使用main函数作为入口点的,java程序也是使用main函数作为入口点。其实Dalvik虚拟机作为应用程序启动时,也是一样的从main函数开始。从Dalvik虚拟机源码目录dalvik/dalvikvm/Main.c文件,就可以看到入口函数,如下:

/*

*
Parse arguments. Most of it just gets passed through to the VM. The

*
JNI spec defines a handful of standard arguments.

*/

int
main(int argc, char* const argv[])

{

JavaVM* vm = NULL;

JNIEnv* env = NULL;

JavaVMInitArgs initArgs;

JavaVMOption* options = NULL;

char* slashClass = NULL;

int optionCount, curOpt, i, argIdx;

int needExtra = JNI_FALSE;

int result = 1;

setvbuf(stdout, NULL, _IONBF, 0);

/* ignore argv[0] */

argv++;

argc--;

…...

因此,当你编译Dalvik虚拟机作为linux下x86应用程序来运行时,就需要注意了,这里是它的入口。

也许你会问,为什么需要Dalvik虚拟机作为linux下x86应用程序运行,它不是在手机平台ARM里运行的吗?这个问题问得好,问到点子上了。其实Dalvik虚拟机作为linux下x86应用程序运行,是用来开发和调试使用的,当需要作为手机平台使用时,它不是从Main函数作为入口点。嵌入式的开发人员都知道,在一个资源有限平台里开发和调试,都是一件不容易的事情,需要花费很多时间,那么有没有更好的方法来开发和调试呢?答案是有的。就是利用目前x86的平台,这样可以使用x86大量的工具和资源,大大地提高开发效率,更容易调试系统的功能。在Dalvik虚拟机进行开发新功能时,可以先在x86的平台上运行和调试通过,然后再编译在ARM平台运行。我曾经修改这个虚拟机的代码后,再在系统里编译和运行,发觉刚刚编译整个平台的时间,就非常多,在4核的CPU、4G内存的linux系统下,再利用make
-j4等多核编译技术,也需要好几分钟,这样每修改几行代码,添加一些细小的功能,就需编译测试一次,付出的时间成本太多了,显然需要另找出路,那么就是使用x86平台方式生成应用程序来测试。还有另外一个问题,就是单步调试的功能,在ARM平台也是比较麻烦的事情,程序有时会跑飞等等。

下面再来分析另外一个入口点,就是android的dalvik虚拟机在ARM平台运行的入口点。它显然不会通过Main函数来加载的,而从初始化进程加载的服务Zygote开始的。从Zygote服务代码,可以看到下面代码:

runtime.start("com.android.internal.os.ZygoteInit",

startSystemServer);

这里根据命令行参数来创建Dalvik虚拟机,并从
com.android.internal.os.ZygoteInit开始运行,然后启动所有Dalvik虚拟服务。
这这里的runtime对象,就是类AppRuntime,而类AppRuntime继承类AndroidRuntime,那么类AndroidRuntime是何物呢?类AndroidRuntime是android平台核心框架里的代码,它是实现JNI的基石,也就是说所有Dalvik虚拟机里的应用程序要访问底层系统的功能,都是通过JNI来实现的。可以从下面的文件找到这个类:

frameworks/base/core/jni/AndroidRuntime.cpp

在这个文件里,可以看到上面调用函数
start的实现,如下:

/*

*
Start the Android runtime. This involves starting the virtual
machine

*
and calling the "static void main(String[] args)" method in
the class

*
named by "className".

*/

void
AndroidRuntime::start(const char* className, const bool
startSystemServer)

{

LOGD("/n>>>>>>>>>>>>>>
AndroidRuntime START <<<<<<<<<<<<<</n");

char* slashClassName = NULL;

char* cp;

JNIEnv* env;

blockSigpipe();

/*

* 'startSystemServer == true' means runtime
is obslete and not run from

* init.rc anymore, so we print out the boot
start event here.

*/

if (startSystemServer) {

/* track our progress through the boot
sequence */

const int LOG_BOOT_PROGRESS_START = 3000;

LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,

ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));

}

const char* rootDir =
getenv("ANDROID_ROOT");

if (rootDir == NULL) {

rootDir = "/system";

if (!hasDir("/system")) {

LOG_FATAL("No root directory
specified, and /android does not exist.");

goto bail;

}

setenv("ANDROID_ROOT", rootDir,
1);

}

//const char* kernelHack =
getenv("LD_ASSUME_KERNEL");

//LOGD("Found
LD_ASSUME_KERNEL='%s'/n", kernelHack);

/* start the virtual machine */

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

goto bail;

/*

* Register android functions.

*/

if (startReg(env) < 0) {

LOGE("Unable to register all android
natives/n");

goto bail;

}

从上面的代码片段里,可以看到调用函数startVm,这个函数就是创建Dalvik虚拟机,继续查看函数startVm的代码片段,可以发现下面一段:

/*

* Initialize the VM.

*

* The JavaVM* is essentially per-process,
and the JNIEnv* is per-thread.

* If this call succeeds, the VM is ready,
and we can start issuing

* JNI calls.

*/

LOGD("JNI_CreateJavaVM Start.../n");

if (JNI_CreateJavaVM(pJavaVM, pEnv,
&initArgs) < 0) {

LOGE("JNI_CreateJavaVM failed/n");

goto bail;

}

LOGD("JNI_CreateJavaVM End/n");

result = 0;

在这段代码里,主要任务就是初始化Dalvik虚拟机,通过调用
JNI_CreateJavaVM函数来实现的,而
JNI_CreateJavaVM函数的实现,就是在文件Dalvik/vm/Jni.c里。

到这里,就已经把另一个入口点分析完成了,通过上面的学习可以知道不同的入口,是来源于现实开发的需要,通过这些大型代码工程,可以学习到很多开发技巧,提高效率的方法。

<!--
@page { margin: 2cm }
P { margin-bottom: 0.21cm }
A:link { so-language: zxx }
-->
//QQ:
9073204 EMAIL:9073204@qq.com

//蔡军生  2011-06-19
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: