1.什么是Zygote
Zygote的字面意思是”受精卵”,我们都知道,有丝分裂的个体最初都是从受精卵发育而来,意思非常明显,在Android系统中也一样,运行新的应用,需要跟Zygote进程结合后才能执行。
我们知道,Android的应用程序是由Java编写的,它们不能直接以本地进程的形态运行在Linux上,只能运行在Dalvik虚拟机中(当然,在5.0之后采用ART了,但是不影响此处的理解)。每个应用程序都运行在各自的虚拟机中,应用程序每次运行都要重新初始化并启动虚拟机,这个过程会耗费相当长时间,是导致应用程序启动慢的原因之一。
为了很好地解决这个问题,在Android中,应用程序运行前,Zygote进程通过共享已运行的虚拟机的代码与内存信息,缩短应用程序运行所耗费的时间。并且,它会预先将应用程序要使用的Android Framework中的类与资源加载到内存中,并组织形成所用资源的链接信息。因此,新运行的Android应用程序在使用所需资源时不必每次重新形成资源的链接信息,从而提高运行速度。
2.Zygote孵化进程分析
2.1 Zygote进程的产生
Android系统启动后运行的首个进程就是init进程。init进程启动完系统运行所需要的各种Daemon后,就启动Zygote进程,这是系统的第一个Zygote进程。该过程如下图所示:
利用adb命令可以看到手机上运行的进程间的派生关系,如下图所示:
从图片的最顶部可以看到zygote的PPID(Parent Process ID,父进程)为1,即init进程,而zygote进程ID为263(注意:这个ID在不同的手机上不一样,因为启动的daemons数量不同),再去查找与263有关的,可以发现launcher,PushService,systemui,location等各种进程的父进程都是zygote,这进一步验证了上面的观点。
2.2 Zygote孵化进程的原理
为了通过比较进程的生成过程,先来看一下在Linux系统中是如何创建一个新进程的。先看下图:
如上图所示,父进程A调用fork()函数创建子进程A1,A1共享A中的内存结构信息与库连接信息,直到子进程A1调用exec(‘B’),将新进程B的代码装载到内存中。此时,父进程A的内存信息被清除,并重新分配内存,以便运行被装载的B进程,接着形成新的库连接信息,以供进程B使用。
那么,在Android中,一个新的应用进程是如何创建并运行的呢?Google在发布Android平台讲述平台特性时曾讲到, Zygote是Android系统的一个主要特征,它通过COW(Copy On Write,我个人将其翻译为写入时才复制)方式对运行在内存中的进程实现了最大程度的复用,并通过库共享有效地降低了内存的使用量.
如下图所示,Zygote进程调用fork()函数创建出子进程Zygote1,它共享父进程Zygote的代码区与连接信息。但是,新的Android应用程序A并非通过fork()来重新装载已有进程的代码区,而是被动态地加载到复制出的Dalvik虚拟机上。而后,Zygote进程将执行流程交给应用程序A的方法,应用程序A开始运行。
如下图所示,由于Zygote启动后,初始化并运行Dalvik虚拟机,而后将需要的类与资源加载到内存中。随后调用fork()创建出Zygote1子进程,接着子进程Zygote1动态加载并运行Andriod应用程序A.运行的应用程序A会使用Zygote已经初始化并启动运行的Dalvik虚拟机代码,通过使用已加载至内存中的类与资源来加快运行速度。
2.3 由app_process运行ZygoteInit class
与其他本地服务或Daemon不同的是,Zygote由Java编写而成,不能直接由init进程启动运行。若想运行Zygote类,必须先生成Dalvik虚拟机,瑞在Dalvik虚拟机上装载运行ZygoteInit类,而执行这一任务的就是app_process进程。下面来分析相应的源码(在frameworks/base/cmds/app_process/app_main.cpp中):
|
|
显然,在main()函数中创建了AppRuntime对象。其中AppRuntime类继承自AndroidRuntime类,而AndroidRuntime类的作用是初始化并运行Dalvik虚拟机,为运行Android应用程序做好准备。
int i=runtime.addVmArguments(argc,argv);的作用是在运行Dalvik虚拟机之前,通过AppRuntime对象,分析环境变量以及运行的参数,并以此生成虚拟机选项。
分析init.rc文件可发现init进程在运行app_process时根据如下规则传递参数:
app_process [java-options] cmd-dir start-class-name [options]
java-options:传递给虚拟机的选项,必须以”-“开始;
cmd-dir:所要运行的进程所在的目录
start-class-name:要在虚拟机中运行的类的名称。app_process会将指定的类加载到虚拟机中,而后调用类的main()方法。
options:要传递给类的选项
app_process服务运行时,init.rc文件中的运行命令如下:
/system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
根据参数规则,可以知道:
1)-Xzygote是指要传递给VM的选项,-Xzygote选项用于区分要在虚拟机中运行的类是Zygote,还是在Zygote中运行的其他Android应用程序,它会被保存在AppRuntime的mOption变量中,如代码int i=runtime.addVmArguments(argc,argv);所示。
2)在代码 runtime.mParentDir = argv[i++]; 中,运行目录参数”syste/bin”被保存到AppRuntime的mParentDir变量中。
3)第三个参数用来指定加载到虚拟机中的类的名称,”–zygote”表示加载com.android.internal.os.ZygoteInit类;
4)最后一个参数”–start-system-server”作为选项传递给生成的类,用于启动系统服务器。
之后调用runtime.start()方法将指定的类加载至虚拟机中,由于代码很简单,不再赘述。
2.4 AndroidRuntime创建虚拟机
下面是AndroidRuntime中创建虚拟机的主要代码:
|
|
显然,start()函数通过调用JNI_CreateJavaVM()函数来创建并运行虚拟机,其函数原型如下:
|
|
JavaVMp_vm:生成的JavaVM类的接口指针
JNIEnvp_env:JNIEnv类的接口指针,方便访问虚拟机
void*vm_args:已设置的虚拟机选项
至于JNI相关的知识,在上一篇博客中已经介绍过了,此处不再进行赘述。
虚拟机创建后,接下来,注册要在虚拟机中使用的JNI函数,调用startReg()方法实现。需要注册的方法数组如下所示:
|
|
2.5 运行ZygoteInit类
在AndroidRuntime的start()函数中,当创建虚拟机并注册相应的本地方法之后,接下来还会运行相应的Java类的main()方法。所谓的相应的Java类是指ZygoteInit类或者与Application相关的ActivityThread类等。下面是主要代码:
|
|
如果认真看了我的博客–JNI完全解析的话,应该很容易理解这里的env->GetStaticMethodId(startClass,”main”,”([Ljava/lang/String:)V”)表示获得main方法的ID,然后在env->CallStaticVoidMethod(startClass,startMeth,strArray); 中调用main()方法。
显然,要了解ZygoteInit做了些什么事情,只需要看一下ZygoteInit的main()方法的代码:
|
|
下一节我们就根据该源码来分析ZygoteInit做了哪些事情。
2.6 ZygoteInit类的功能
在上一小节的最后我们给出了ZygoteInit的main()函数代码,并且给出了相应的注释。概括起来,主要完成了以下4个工作:
- 1)registerZygoteSocket(); 语句用来绑定socket,以便接收新的Android应用程序的运行请求。为了从ActivityManager接收新的Android应用程序的运行请求,Zygote使用UDS(Unix Domain Socket),init进程在运行app_process时,使用init.rc文件中以”/dev/zygote”形式注册的socket;
- 2)该行用于将应用程序框架中的类、平台资源(图像、XML信息、字符串等)预先加载到内存中。新进程直接使用这些类与资源,而不需要重新加载它们,这大大加快了程序的执行速度;
- 3)通过app_process运行zygote时,参数”–start-system-server”会调用startSystemServer()方法启动系统服务器,系统服务器用来运行Android平台需要的一些主要的本地服务(主要是SurfaceFlinger,AudioFlinger,MediaPlayerService,CameraService等);
- 4)runSelectLoopMode();的作用是监视UDS,若收到新的Android应用程序生成请求,则进入处理循环.
用一张图来表示ZygoteInit的main()方法完成的功能就是:
关于以上4个工作具体是如何实现的,将在下一篇博客中详细解读,敬请关注。