Android启动流程概览:从按下电源到Launcher显示

说实话,做埋点不搞懂启动流程,就像修车不看发动机内部——早晚要翻车。我当年刚接触系统级埋点时,就因为在Boot阶段丢了一段关键日志,排查了整整三天……嗯,从那以后我养成了一个习惯:按下电源键的那一刻起,大脑就要跟着代码一起跑。

你为什么要关心这个流程?

启动埋点的核心难点在于:系统还没完全起来,埋点基础设施可能都没加载。你想想看,如果往一个还没初始化的文件系统里写日志,要么写不进去,要么进程被kernel杀掉。所以,搞清每个阶段能做什么、不能做什么,是必修课。

整个启动过程可以拆成几个关键阶段。我把它整理成下面这张图,方便你对照着看:

Android 启动流程核心阶段 按下电源 → Boot ROM BootLoader 启动 Linux Kernel 启动 init 进程启动 Zygote 孵化器 SystemServer 启动系统服务 Home (Launcher) 显示 💡 每个阶段可用的埋点能力不同:Boot ROM 阶段只有串口输出;init 之后才有文件系统; Zygote 之后才能使用 Java 层的 Log 系统。

阶段拆解:每个环节都在干什么

阶段主导者关键动作埋点约束
1. 按下电源 硬件 + Boot ROM 加载SoC厂商的预置代码,校验签名 没有存储,只能通过UART输出
2. BootLoader BootLoader(如uboot、ABL) 初始化DDR、加载boot.img到内存 可以写日志到内存缓冲区,断电丢失
3. Linux Kernel Linux内核 驱动加载、设备树解析、挂载根文件系统 可通过printk输出,但无法持久化
4. init进程 init(pid=1) 解析init.rc,启动核心守护进程 可以写文件到 /data 分区(挂载后)
5. Zygote Zygote进程 预加载Java framework类,监听socket 可以调用 Android.util.Log
6. SystemServer SystemServer进程 启动AMS、WMS、PMS等系统服务 可全量使用埋点SDK(如果已集成)
7. Launcher Launcher应用 显示桌面图标,接收开机广播 标准的Android埋点环境

注意看,从第4阶段(init进程)开始,埋点才有了“写文件”的可能性。我之前有个项目需要在BootLoader阶段记录一次硬件自检结果,结果发现数据存在内存里,重启后就没了……后来改成通过misc分区缓存,才解决了问题。

埋点能从哪里下手?

不同阶段留“证据”的方法完全不同。我个人习惯在init.rc里加一个服务来做壳埋点,比如这样:

# 在 /system/etc/init/boot_log.rc 中添加
service boot_logger /system/bin/boot_logger
    class core
    user root
    oneshot
    # 在boot阶段就启动,记录关键节点时间戳
    writepid /sys/class/.../boot_complete

然后在boot_logger的代码里往 /data/boot_trace 写文件。不过要注意 —— /data 分区在早期可能是加密的,需要等vold启动后才能正常写入。这个坑我踩过,日志全都丢在了/data/之前。后来我改用/proc/来中转,虽然不太优雅,但至少数据没丢。

👉 实战技巧:如果你只需要知道“启动花了多少秒”,就在SystemServer的ready回调里打一个时间戳,然后在Launcher onResume里再打一个。差值就是用户感知的启动时间。

一个常见的坑:init阶段的并发问题

init.rc里的服务是按类(class)顺序启动的。比如class core比class main先跑。但我遇到过一种情况:我们的埋点服务依赖了某个驱动节点,结果那个驱动还没加载完成,埋点服务就启动了,导致一直读不到数据。解决办法也很简单——用 on property:sys.boot_completed=1 来延迟启动埋点服务。嗯,这里要注意,sys.boot_completed 这个属性是SystemServer在启动完成后设置的,用它触发最保险。

⚠️ 注意事项:千万别在Boot ROM或BootLoader阶段尝试写sdcard —— 这时候MMC都没初始化,写了就是程序崩溃。我曾经在原型机上试过,结果设备直接黑屏,只能重新刷机。

总结一下本章的核心点

  • 时机决定一切:清楚每个阶段能访问什么资源,埋点才能落地。
  • init.rc是你最强大的武器:可以定义服务、设置属性、控制启动顺序。
  • 善用/proc和/sys:在早期阶段,这两个虚拟文件系统是最可靠的通信渠道。
  • 多留一条后路:如果主路径写失败,及时降级到日志缓冲区或内核日志。