一、启动流程概览:Android系统启动的完整链路
Android系统启动,说白了就是一部「从按下电源键到桌面亮起」的连续剧。我做了这么多年系统优化,每次跟新人聊启动流程,都喜欢先画一条时间线——因为只有知道谁先谁后、谁依赖谁,你才能理解为什么某个阶段会卡住,为什么某个优化手段有效。
今天我们就从头到尾捋一遍这条链路:BootROM → Bootloader → Kernel → Init → Zygote → SystemServer。每个阶段的核心任务是什么?耗时分布长什么样?我会结合我踩过的坑,给你讲清楚。
1.1 整体链路图
先上一张我手绘的流程图。你盯着看30秒,后面再拆开讲,会轻松很多。
这张图里,每个阶段我都标了典型的耗时区间。你可能会问:「为什么Zygote和SystemServer占了将近一半的时间?」嗯,这里我先卖个关子,后面会细讲。
1.2 各阶段核心任务拆解
1.2.1 BootROM —— 芯片出厂就写死的「第一行代码」
BootROM 是固化在 SoC 内部 ROM 里的微代码。你没法改它,也没法跳过它。它的任务极其简单:上电后初始化最基本的硬件(时钟、电压、RAM控制器),然后从预定义的存储位置(eMMC/UFS)加载 Bootloader 到 SRAM 中执行。
我个人习惯把 BootROM 叫做「系统启动的助跑阶段」。它不干任何复杂的事,但一旦 BootROM 坏了,手机就是一块砖头——这就是所谓的硬砖。
1.2.2 Bootloader —— 真正的「看门人」
Bootloader 是运行在 DRAM 中的第一段可编程代码。它通常分两个阶段:
- 第一阶段(Primary Bootloader, PBL):由 BootROM 加载,初始化 DRAM 控制器,为第二阶段腾出空间。
- 第二阶段(Secondary Bootloader, SBL):真正的重头戏。它负责加载 boot.img(包含 kernel + ramdisk),校验签名,然后跳转到 kernel 入口。
我记得有一次在某个项目上,Bootloader 阶段花了整整 3 秒。查了半天,发现是显示初始化(Display Init)占了 1.5 秒——厂商在 Bootloader 里做了开机动画。你想想看,开机动画在 Bootloader 阶段播放,用户看到 logo 亮了,但系统其实还没开始跑,这种体验其实挺坑的。
1.2.3 Kernel —— 操作系统的「地基」
Kernel 启动后,会做以下几件核心的事:
- 初始化 CPU 子系统:MMU、中断控制器、定时器。
- 驱动初始化:GPIO、I2C、SPI、UART、存储、网络等。
- 挂载根文件系统:从 ramdisk 或实际分区挂载 /。
- 启动 init 进程:PID 1,用户空间的第一个进程。
这里有个关键点:Kernel 的启动时间很大程度上取决于驱动加载的顺序和方式。比如,如果你的存储驱动是模块化加载(built as module),那就要等文件系统挂载后才能加载——这就形成了死锁。所以,存储驱动必须编译进内核(built-in)。
我见过一个项目,Kernel 阶段花了 2.8 秒,其中 1.2 秒花在了等待某个 I2C 触摸屏驱动 probe 超时上。解决方案?把那个驱动改成异步 probe,或者干脆延迟加载。说白了,Kernel 启动优化就是「能并行的别串行,能延迟的别着急」。
1.2.4 Init —— 用户空间的「大管家」
Init 进程是 Android 用户空间的第一个进程。它的核心工作是解析 init.rc 及其导入的脚本,然后按顺序启动各种服务。
Init 阶段的主要任务包括:
- 挂载关键分区(system、vendor、data 等)
- 设置 SELinux 策略(加载 sepolicy)
- 启动核心服务:ueventd、logd、servicemanager、surfaceflinger 等
- 最后启动 Zygote
1.2.5 Zygote —— Android 的「进程孵化器」
Zygote 是 Android 框架层的起点。它启动后会做两件大事:
- 预加载 Java 框架类:加载所有常用的 Android 框架类(Activity、Service、View 等),大概有 3000+ 个类。
- 启动 SystemServer:通过 fork 自身创建 SystemServer 进程。
Zygote 的预加载机制,说白了就是「一次加载,到处使用」。所有 App 启动时都从 Zygote fork 出来,共享已经加载好的类和数据。这样 App 启动就快多了。
但这里有个问题:预加载的类越多,Zygote 启动就越慢。我记得 Android 8.0 之前,Zygote 启动要 3~4 秒。后来 Google 做了优化,把一些不常用的类改成了「按需加载」,才降到了 2 秒左右。
preloaded-classes 文件,把那些你确定用不到的类去掉。但小心——删错了会导致 App 启动时 ClassNotFoundException。
1.2.6 SystemServer —— 系统服务的「总指挥部」
SystemServer 是 Android 系统中最重要的进程。它启动后,会依次启动几十个系统服务:
- 核心服务:ActivityManagerService、WindowManagerService、PackageManagerService
- 硬件服务:PowerManagerService、BatteryService、SensorService
- 媒体服务:MediaServer、AudioService
- 其他:ConnectivityService、LocationManagerService 等
SystemServer 的启动是串行的——每个服务都要等前一个服务初始化完成才能开始。所以,如果某个服务初始化特别慢,整个启动时间就被拉长了。
我遇到过最夸张的一个 case:某个厂商在 PackageManagerService 里扫描所有已安装 App 的签名,耗时 4 秒。后来改成「首次启动全量扫描,后续增量扫描」,直接砍掉了 3 秒。
1.3 耗时分布总结
下面这张表是我基于多个项目经验总结的典型耗时分布。注意,不同硬件和 Android 版本会有差异,但大方向是一致的。
| 阶段 | 典型耗时 | 主要耗时原因 | 优化空间 |
|---|---|---|---|
| BootROM + Bootloader | 0.3~0.8s | 硬件初始化、签名校验 | 低(硬件限制) |
| Kernel | 1.0~2.0s | 驱动加载、文件系统挂载 | 中(异步 probe、裁剪驱动) |
| Init | 0.8~1.5s | SELinux 加载、fsck、服务启动 | 高(压缩 sepolicy、并行启动) |
| Zygote | 1.5~2.5s | 预加载 Java 类 | 中(按需加载、精简类列表) |
| SystemServer | 2.0~3.5s | 串行启动系统服务 | 高(并行化、延迟初始化) |
| Launcher | 1.0~2.0s | 加载桌面、恢复应用 | 中(预创建、缓存) |
从这张表你可以看到:SystemServer 和 Zygote 是启动优化的主战场,两者加起来占了总耗时的一半以上。Init 阶段虽然时间不长,但优化空间大,也值得关注。
1.4 我的一点经验总结
做启动优化这么多年,我最大的感受是:不要盲目优化,先搞清楚瓶颈在哪。我见过有人花了两周优化 Kernel 启动,结果只快了 0.2 秒——而 SystemServer 里一个服务优化一下就能快 1 秒。
所以,我的建议是:
- 先测量,再优化。用
bootchart或systrace抓出各阶段的精确耗时。 - 抓大放小。先解决耗时最长的阶段,别在 0.1 秒的优化上钻牛角尖。
- 注意副作用。有些优化(比如延迟加载服务)可能会导致第一次使用该服务时变慢,需要权衡。
好了,启动流程概览就讲到这里。下一节我们会深入 Init 进程,看看它是怎么解析 init.rc 的,以及 SELinux 策略到底是怎么加载的——那才是真正有意思的部分。