1、AOSP启动流程概述:启动阶段划分
做埋点之前,你得先知道系统是怎么活过来的。AOSP的启动就像一场接力赛,每个阶段跑完一棒,把控制权交给下一棒。我个人习惯把这六个阶段记成一句话:Bootload开机 → Kernel拉起内核 → Init启动用户态 → Zygote孵化Java世界 → SystemServer加载服务 → Launcher呈现桌面。下面我们逐个拆开看。
你想想看,从按下电源键到看到桌面,手机里到底发生了什么?这中间任何一个环节慢了,用户都能感知到「怎么还没点亮?」「怎么黑屏这么久?」。我曾经在优化开机时间时,发现某款设备的Bootloader阶段就卡了800ms,原因居然是引导分区读了个没用的日志文件。嗯,埋点就是要精确抓住这些坑。
1. Bootloader 阶段
按下电源键,ROM里的一段固化代码开始跑。Bootloader负责初始化硬件、检测启动模式(正常启动、recovery、fastboot等),然后把内核镜像加载到内存并跳转。这个阶段通常很短,但容易踩坑——比如我遇到过某款低端机,bootloader里塞了个全量校验,导致每次开机多耗1.2秒。埋点只需要打一个时间戳(进入bootloader和离开bootloader),就能算出这一段耗时。
2. Kernel 阶段
Linux内核接管硬件。这里做的事情很重:初始化内存管理、调度器、驱动加载(显示、存储、网络等),最后挂载根文件系统并执行第一个用户态进程——init。Kernel阶段的埋点比较特殊,内核空间不能用Java打的log,得靠printk或者tracepoint。我习惯在start_kernel函数入口和kernel_init完成处加时间戳,配合dmesg来解析。
// 内核埋点示例(伪代码)
trace_printk("KERNEL_INIT_START\n");
// ... 一系列初始化 ...
trace_printk("KERNEL_INIT_DONE\n");
3. Init 阶段
init进程(PID=1)启动后,它读取/init.rc脚本,按顺序执行一系列动作:挂载分区、设置属性、启动关键守护进程(如ueventd、logd、servicemanager等)。这个阶段是AOSP用户态的第一个里程碑。埋点可以打在init.rc每个section的开头和结尾,但别打在service启动里,那太细了。我的做法是用property_set("sys.init_completed", "1")当信号,然后查系统属性变化时间来算。
4. Zygote 阶段
Init会启动zygote进程,它是Java世界的始祖。Zygote加载Java虚拟机,预加载常用类库和资源(比如系统字体、图标、主题),然后监听socket等待创建新应用进程。这个阶段有两个关键时间点:zygote进程开始执行Java代码(ZygoteInit.main),以及zygote完全就绪(开始监听socket)。我个人习惯在ZygoteInit.java里加埋点:
// frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
// ... 预加载逻辑 ...
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
说白了,zygote预热的时间直接决定了「冷启动」第一个应用的速度。如果你想优化开机时间,这里往往是最大的优化空间——把不必要的预加载类移走,我就这么干过。
5. SystemServer 阶段
Zygote fork出system_server进程,它是Android系统的「大管家」。这个阶段启动的Java服务多得吓人:ActivityManagerService、PackageManagerService、WindowManagerService……光核心服务就20多个,而且很多服务之间有互相依赖。埋点要细化到每个服务的onStart和systemReady回调。我建议用表格记录每个服务的耗时:
| 服务名称 | 启动顺序 | 典型耗时(ms) | 埋点标签 |
|---|---|---|---|
| Installer | 1 | ~80 | installer_start/end |
| ActivityManagerService | 3 | ~200 | ams_ready |
| PackageManagerService | 5 | ~1200 | pms_scan_end |
| WindowManagerService | 7 | ~150 | wms_init |
对,PackageManagerService经常是耗时大户,因为它要扫描所有已安装应用的APK。我见过一台预装200个应用的设备,PMS扫描用了3秒多。
6. Launcher 阶段
SystemServer启动完成后,会发送一个ACTION_BOOT_COMPLETED广播,但用户真正能交互是在Launcher(桌面)显示出来之后。Launcher进程由zygote孵化,它加载主界面、绘制图标并开始响应触摸。这个阶段的埋点要盯住Activity.onCreate和onResume的时间差。我觉得最直观的指标是「冷启动到Launcher第一帧显示」的时长,可以用Choreographer来获取第一帧的时间。
好了,这六个阶段就是AOSP启动的主干。每个阶段的关键事件我都标出来了,你可以对照着先把手动日志打一遍。下一步我们就会深入每个阶段,讲具体的埋点API和实战技巧。先消化这张图,有问题随时问我。