zygote的启动
zygote 的功能
zygote 进程的主要功能有两个:
- 在系统启动时启动 SystemServer 进程
- 启动 应用进程
zygote 进程的启动过程
zygote进程的创建。
在 Android 系统开机之后,会创建 init 进程,这是 Linux 系统启动后创建的第一个进程。 init 进程有一个配置文件 init.rc 。 将需要启动的服务写入到 init.rc 中, init 进程在启动后就会创建这些进程, 比如 zygote , ServiceManager 等。
zygote 进程的 native 部分。
zygote 的c层代码入口在 /rameworks/base/cmds/app_process/app_main.cpp 中,最终主要的工作内容在 /frameworks/base/core/jni/AndroidRuntime.cpp 。主要内容如下。不详细分析了
- 启动虚拟机。
- 注册 JNI 函数
- 调用 Java 类 Zygote.main 方法,进入 Java 循环 。
zygote 进程的 Java 部分
zygote 的 java 入口在 /android_aosp/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java 中
1 | // /android_aosp/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java |
- 注册 Socket 服务, zygote 进程通过 socket 和 AMS 通信
- 预加载类和资源。这样在 fork SystemServer 和 应用进程时,能够直接拷贝给子进程使用
- fork 出 SystemServer 进程, SystemServer 进程随后又会启动 Binder ,并启动这种系统 Service ,比如 AMS ,WMS 等。
- 进入 loop 循环,等待接收 AMS 的 Socket 消息。 收到消息后会调用 runOnce 方法,fork 出应用进程,并执行应用进程的 ActivityThread.main()
小结:
注意:zygote fork 进程时一定是单线程的
- zygote 新 fork 出来的进程不需要再创建java虚拟机,因为 fork 出来的子进程是会继承拷贝父进程的资源的, fork 出来的进程会拥有 zygote 进程的虚拟机的拷贝
- 为什么要设计一个 zygote 来 fork 应用进程,而不是 SystemServer :
- 因为systemServer中运行了很多系统服务,不能被继承。应用程序启动时,除了必要的资源外,最好是干净的,所以需要单独用一个zygote来fork。
- 为什么Zygote要使用Socket不使用binder:
- 因为binder机制支持并发有一个线程池,如果zygote支持并发,在fork之后,会把线程池中的线程一起复制一份。 如果zygote的binder线程池处于一个等待锁的状态,fork之后的新进程依然是在等待锁的,并且不能解锁。 从而有可能造成死锁
zygote 的事件循环
1 | // /frameworks/base/core/java/com/android/internal/os/ZygoteServer.java |
1 | // /frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java |
应用进程的启动
我们以 Activity 的启动为入口,来探讨应用进程的启动过程
AMS 发起创建进程
由 Activity 的启动寻找应用进程创建的入口
首先来看 Activity 的启动。安卓四大组件都是由AMS来管理的。 应用进程会调用 AMS 的binder 对象,实现在 ActivityManagerService.startActivity 里。
最终顺着调用栈,会走到 ActivityStackSupervisor.startSpecificActivityLocked(…)
1 | // /frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java |
上面的逻辑里,会先判断要启动的 activity 所在应用的进程是否启动,如果已经成功启动,则开始启动 activity ;否则,启动引用进程。
注意这里应用是否启动的判断条件: if (app != null && app.thread != null)
ProcessRecord 是应用进程在 AMS 的信息记录,除了判断 app 不为空,还对 app.thread 。这个 app.thread 是一个 IApplicationThread binder 对象,这个对象很重要。
AMS 发送 Socket 给 zygote
1 | "this") ( |
AMS.startProcess 会调用 Process.start(…) 方法
1 | // /frameworks/base/core/java/android/os/Process.java |
AMS 会发送 socket 消息给 zygote ,zygote 会 fork 应用进程,并执行应用进程的 ActivityThread.main 方法
应用进程在启动之后都干了啥?
thread.attach(…)
1 | // frameworks/base/core/java/android/app/ActivityThread.java |
应用整个进程的入口在 ActivityThread.main() 方法中,这个方法里没啥操作: 1. 准备 Looper 2. 实例化 ActivityThread ,并 attach 3. 开启 Looper 循环
显然, main 方法里做的事情支撑不了一个 app 的启动。 重点看 thread.attach , 它 使用 binder 调用了 AMS.attachApplication ,然后又调用了 AMS.attachApplicationLocked(…)
1 | // 伪代码。。 |
thread.bindApplication(…)
AMS 在 attch 进程之后又通过 binder 调用了应用进程的 bindApplication
1 | public final void bindApplication(String processName, ApplicationInfo appInfo, |
在应用进程,发送了一个 H.BIND_APPLICATION 给 handler ,实现了线程的切换。 注意 通过 binder对象调用,是运行在 binder 线程里的,所以这个 bindApplication 也是运行在 binder 线程里的,需要切换回主线程。
handleBindApplication
1 | // 伪代码 |
data.info 是一个描述应用安装包信息的类。 data.info.makeApplication
一个applicaiton 类,并调用生命周期的 onCreate
1 | // frameworks/base/core/java/android/app/LoadedApk.java |
这样,应用的启动就完成了