三个值得深入思考的 Android 问答分享(第 2 期)
这是 JsonChao 的第 181 期分享
一、Android 系统启动
1、Android 有哪些主要的系统进程?
主要有 zygote、servicemanager、surfaceflinger、mediaserver 等系统进程。
2、Zygote 是怎么启动的?
Zygote 的启动流程分为 5 步:
1)、init 进程 fork 出 zygote 进程。 2)、启动虚拟机,注册 jni 函数。 3)、预加载系统资源。 4)、启动 SystemServer。 5)、进入 Socket Loop。
3、SystemServer 是怎么启动的?
启动 Binder 线程池和 SystemServiceManager,然后启动各种系统服务(引导服务、核心服务与其它服务)。
4、启动 Binder 线程池的流程?
1)、打开 binder 驱动(128kb)。 2)、mmap 映射内存,分配缓冲区。 3)、启动 binder 线程,进入 binder loop(可以创建一个子线程把它注册成 binder 线程,也可以直接把应用的主线程注册成 binder 线程)。
binder loop 做了什么处理?
在 binder_loop 方法中,调用了 binder_write,其最终是调用的 ioctl 系统调用,ioctl 系统调用的细节有 2 点:
1)、给 binder_write_read 结构体的 write_buffer 设置缓冲区数据的第一个值为 BC_ENTER_LOOPER 时,表示把当前线程注册为 binder 线程。 2)、配合 BINDER_WRITE_READ, 当 binder_write_read 的 write_size 大于 0、read_size 等于 0,则执行写,反之,则执行读,如果两者都大于 0,则先执行写,再执行读。
通过 ioctl 不断地读数据到 binder_write_read 对象中,最后再解析数据回调给 func 函数。
5、如何发布系统服务?
执行 ServiceManager.addService(name, service, allowIsolated) 方法即可。
6、系统服务跑在什么线程?
有两类线程:
1、binder 线程
应用跨进程调用后肯定首先都是在 binder 线程。
2、工作线程
DisplayThread(显示线程) FGThread(后台线程) IOThread UIThread
7、Android 系统是如何解决系统服务之间的互相依赖的?
主要有两种方式:
1)、分批启动:比较基础的 Service 放前面,例如 AMS、PMS、PKMS,上层一点的 Service 就可以放后面了。 2)、分阶段启动:每到一个阶段就可以通知当前的 Service,Service 就可以去做一些这个阶段可以做的初始化。
8、桌面启动时做了什么?
调用 startHomeActivityLocked 方法启动一个桌面 Activity 即 launch,它里面会启动一个 LoaderTask。
LoaderTask会执行 mPm.queryIntentActivitiesAsUser 方法去向 PKMS 查询所有已安装的应用,查询到之后再把这些应用的图片显示到桌面上。
9、系统是怎么帮我们启动找到桌面应用的?
通过 Intent 意图,PMS 会解析所有 apk 的 AndroidManifest.xml ,如果解析过会存到 package.xml 中,不会反复解析,PMS 有了它就能找到了。
10、Android 系统的启动流程是怎样的?
提示:init 进程 -> Zygote 进程 –> SystemServer 进程 –> 各种系统服务 –> 应用进程。
1、启动电源以及系统启动:按电源键通电后,引导芯片启动,引导芯片开始从固化在 ROM 里的预设代码执行,加载引导程序 bootloader 到 RAM。 2、引导程序 BootLoader:BootLoader 是在 Android 系统开始运行前的一个小程序,它会检查 RAM,初始化硬件参数,主要用于把系统 OS 拉起来并运行。 3、Linux内核启动:当内核启动时,会加载一些硬件设备驱动,初始化进程管理,内管管理、加载 Driver 驱动文件等,启动内核核心进程。当其完成系统设置时,会先在系统文件中寻找 init.rc 文件,并启动 init 进程。 4、init 进程启动:它启动后会启动 adbd,logd 等用户守护进程,同时 fork 出 zygote 进程。 5、Zygote 进程启动:zygote 进程是承上启下的存在,同时会反射调用 com.android.internal.os.ZygoteInit 进入 Java 层。然后创建 JVM 并为其注册 JNI 方法,创建服务器端 Socket,启动 SystemServer 进程。 6、SystemServer 进程启动:启动 Binder 线程池和 SystemServiceManager,并且启动各种系统服务。 7、Launcher 启动:被 SystemServer 进程启动的 AMS 会启动 Launcher,Launcher 启动后会将已安装应用的快捷图标显示到系统桌面上。
二、应用进程启动
1、你知道应用进程是怎么创建的吗?
主要有两种方式:
1)、fork:返回 pid 小于 0 表明发生错误,返回 pid 等于 0 则表明是在子进程中,其它请求则是在父进程中。 2)、fork + execve:同 fork 逻辑类似,不同的是在子进程中需要调用 execve(path, argv, env) 方法去加载另一个二进制程序,子进程的资源会完全被新的二进制程序替换。
2、什么时候触发的进程启动?
由 AMS 通过 socket 通信向 zygote 发起,zygote 会 fork 出应用进程,并执行 ActivityThread 的 main 函数,进程启动之后向 AMS 报告,整个启动才算结束。
具体是通过 startProcessLocked(r.processName) 启动的,它的主要逻辑为 2 步:
1)、通过 socket:openZygoteSocketIfNeeded 打开本地 socket。 2)、调用 zygoteSendArgsAndGetResult 发送参数列表和得到返回创建的进程 ID。
3、App 的启动流程(Activity 的冷启动流程)?
大致原理
AMS 在启动应用程序时会检查这个应用程序需要的应用程序进程是否存在,不存在就会请求 Zygote 进程启动需要的应用程序进程。
在 Android 系统启动流程中 Zygote Java 框架层中会创建 Server 端的 Socket ,这个 Socket 用来等待 AMS 请求 Zygote 来创建新的应用程序进程。
Zygote 进程通过 fork 自身创建应用程序进程,这样应用程序进程就会获得 Zygote 进程在启动时创建的虚拟机实例。
当然,在应用程序进程创建过程中除了获取虚拟机实例外,还创建了 Binder 线程池和消息循环,这样运行在应用进程中的应用程序就可以方便地使用 Binder 进行进程间通信以及处理消息了。
在点击应用图标后会去启动应用的 Launcher Activity,如果 Launcer Activity 所在的进程没有创建,还会创建新进程,整体的流程就是一个 Activity 的启动流程。
整个流程涉及的主要角色
1)、Instrumentation:监控应用与系统相关的交互行为。 2)、AMS:组件管理调度中心,什么都不干,但是什么都管。 3)、ActivityStarter:Activity 启动的控制器,处理 Intent 与 Flag 对 Activity 启动的影响,具体说来有 3 点: 1、寻找符合启动条件的 Activity,如果有多个,让用户选择。 2、校验启动参数的合法性。 3、返回 int 参数,代表 Activity 是否启动成功。 4、ActivityStackSupervisior:这个类的作用你从它的名字就可以看出来,它用来管理任务栈。这是高版本才有的类,它用来管理多个 ActivityStack,早期的版本只有一个 ActivityStack 对应着手机屏幕,后来高版本支持多屏以后,就有了多个 ActivityStack,于是就引入了 ActivityStackSupervisior 用来管理多个 ActivityStack。 5、ActivityStack:用来管理任务栈里的 Activity。 6、ActivityThread:最终干活的人,Activity、Service、BroadcastReceiver 的启动、切换、调度等各种操作都在这个类里完成。
整个流程主要涉及的四个进程
1)、调用者进程:如果是在桌面启动应用就是 Launcher 应用进程。 2)、ActivityManagerService 等待所在的 System Server 进程:该进程主要运行着系统服务组件。 3)、Zygote 进程:该进程主要用来 fork 新进程。 4)、新启动的应用进程:该进程就是用来承载应用运行的进程了,它也是应用的主线程(新创建的进程就是主线程),处理组件生命周期、界面绘制等相关事情。
有了以上的理解,整个流程可以概括如下:
1、点击桌面应用图标,Launcher 进程将启动 Activity 的请求以 Binder 的方式发送给了 AMS。 2、AMS 接收到启动请求后,交付 ActivityStarter 处理 Intent 和 Flag 等信息,然后再交给 ActivityStackSupervisior/ActivityStack 处理 Activity 进栈相关流程。同时以 Socket 方式请求 Zygote 进程 fork 新进程。 3、Zygote 接收到新进程创建请求后 fork 出新进程。 4、在新进程里创建 ActivityThread 对象,新创建的进程就是应用的主线程,在主线程里开启 Looper 消息循环,开始处理创建 Activity。 5、ActivityThread 利用 ClassLoader 去加载 Activity、创建 Activity 实例,并回调 Activity 的 onCreate() 方法,这样便完成了 Activity 的启动。
4、在 invokeStaticMain 方法中 throw new Zygote.MethodAndArgsCaller(m, argv) 而不直接调用 ActivityThread 的 main 方法的用意?(消息循环创建过程)
在 Runtimelnit.invokeStaticMain() 中,会抛出一个 MethodAndArgsCaller 异常,这个异常会被 Zygoteinit 的 main 方法捕获,并执行 caller 的 run 方法。
然后,会通过 mMethod.invoke 反射的方式调用 ActivityThread 的 main 方法
最后,会在其中启动主线程的消息循环。
三、应用如何启动 Binder 机制?
1、应用的大致启动流程是怎样的?
可以简单理解为:AMS 请求 => Zygote 启动进程 => 应用启动好了 => 通知 AMS。
2、怎么启用 Binder 机制?
进程启动初始化的时候,在 ProcessState 的构造方法中会启动 binder 机制。
详细流程
1)、在 Zygotelnit 类的 zygoteInit 方法中,最终会调用到 AppRuntime 的 onZteInit 函数,它会调用 ProcessState 的 startThreadPool 函数来启动 Binder 线程池。 2)、如果 Binder 线程池没有启动过,则调用 spawnPooledThread 函数来创建线程池中的第一个线程,也就是线程池的主线程。 3)、在线程的 run 函数中,会调用 IPCThreadState 的 joinThreadPool 函数,将当前线程注册到 Binder 驱动程序中,这样我们创建的线程就加入了 Binder 线程池当中,新创建的应用程序进程就支持 Binder 进程间通信了。我们只需要创建当前进程的 Binder 对象,并将它注册到 ServiceManager 中就可以实现 Binder进程间通信,而不必关心进程间是如何通过 Binder 进行通信的。
今天分享的内容仅仅是 Android Framework 系统中的冰山一角,这两年,我打造了一份体系化的 Framework 通关秘籍,这份通关秘籍的全貌如下所示:
如果还想查看上述更多的高频核心 Framework 问答分享,请扫描下方二维码查看:
END
参考链接:
1、Android V9.0.0 源码
2、Android 系统开篇
http://gityuan.com/android/
3、Android 进阶解密第二章
https://book.douban.com/subject/30358046/
4、android zygote 进程启动到 SystemServer 进程启动过程
https://blog.csdn.net/luoyingxing/article/details/87277078
点击下方卡片关注 JsonChao,为你构建一套
未来技术人必备的底层能力系统
▲ 点击上方卡片关注 JsonChao,构建一套
未来 Android 开发必备的知识体系
欢迎把文章分享到朋友圈
星球门票出售火爆,本期星球优惠券仅剩最后 10 张,先到者先得,错过再无。