【全栈第3课】SystemServer 启动流程 + Service 三阶段生命周期
Android 全栈工程师进阶教程 第03课。SystemServer 启动流程 + Service 三阶段生命周期。基于公开 AOSP 知识整理,含原理、可编译示例、常见问题定位。
> 本文是《Android 全栈工程师进阶教程》系列第 03 课。完整 26 课见 GitHub 仓库。
【全栈第3课】SystemServer 启动流程 + Service 三阶段生命周期
0. 这节课你将做出什么
给 L2 的 HelloManagerService 加上 onBootPhase 多阶段生命周期,刷机后从 logcat 亲眼看到:
onStart 先执行,然后各个 boot phase(500→550→600→1000)按数字顺序依次触发。
学完你能回答:为什么系统服务启动有严格先后顺序?为什么有些初始化能放 onStart,有些必须等到某个 bootPhase,放错了就 NPE?(这是你刷机后"服务起不来/初始化崩溃"最常见的根因之一)
1. 背景:光"启动"还不够,得管"启动顺序"
L2 我们让服务开机能起了。但真实系统有 100+ 个服务,它们互相依赖:IMS 启动时要用 PowerManager,PowerManager 又要先于 IMS 起好。如果顺序乱了,后起的服务调先起的 → 拿到 null → NPE。
更麻烦的是:一个服务的 onStart() 被调用时,并不代表它依赖的所有服务都好了——因为排在它后面启动的服务还没 onStart。
Android 用两个机制解决:
- 分批 + 固定顺序启动(解决"谁先谁后")
- boot phase 广播(解决"启动了但还没准备好")
2. 全局图:三批启动 + boot phase 广播
SystemServer.run()
│
├─ startBootstrapServices() 第1批:最底层,别人都依赖 → AMS/PMS/PowerManager/Watchdog
├─ startCoreServices() 第2批:核心 → BatteryService/UsageStats/WebViewUpdate
└─ startOtherServices() 第3批:绝大多数 → IMS/WMS/HelloService 都在这
│
▼ 所有服务 onStart() 都执行完后
▼ SystemServer 一轮轮"广播" boot phase:
PHASE_SYSTEM_SERVICES_READY(500) → 所有服务的 onBootPhase(500)
PHASE_ACTIVITY_MANAGER_READY(550) → 所有服务的 onBootPhase(550)
PHASE_THIRD_PARTY_APPS_CAN_START(600) → ...
PHASE_BOOT_COMPLETED(1000) → ...
两个时间点的区别(关键):
| 回调 | 什么时候调 | 能安全做什么 |
|---|---|---|
onStart() | 服务被 startService 时,立即 | 只做不依赖别人的事:注册自己、建数据结构 |
onBootPhase(phase) | 所有服务 onStart 完后,按 phase 分轮 | 依赖别的服务的事,等到对应 phase 才做 |
3. 某真实机型 真实源码印证
三批启动(SystemServer.java):
run() 里依次调用: :1197 startBootstrapServices(t);
:1201 startCoreServices(t);
:1205 startOtherServices(t);
private void startBootstrapServices(...) :1372 // AMS/PMS/PowerManager...
private void startCoreServices(...) :1722
private void startOtherServices(...) :1808 // IMS/HelloService 在这批
boot phase 常量定义(SystemService.java,数字越大越晚):
PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100 显示就绪
PHASE_LOCK_SETTINGS_READY = 480
PHASE_SYSTEM_SERVICES_READY = 500 ← 过了才能安全调 PowerManager/PackageManager
PHASE_DEVICE_SPECIFIC_SERVICES_READY = 520
PHASE_ACTIVITY_MANAGER_READY = 550 ← 过了才能发广播 Intent
PHASE_THIRD_PARTY_APPS_CAN_START = 600 ← 三方App可启动,App能Binder调你
PHASE_BOOT_COMPLETED = 1000 ← 开机完成,Home起,可与用户交互
这些常量上面的注释(AOSP 原文)直接写明了"过了这个 phase 才能安全做什么"——这是官方契约。
> 你的 HelloService 加在 StartInputManagerService 旁边,所以在第 3 批 Other。这时 AMS/PMS/PowerManager(第1批)都就绪了,所以在 onStart 里用它们是安全的。
4. 前置准备
- 依赖 L2 的产物:
HelloManagerService(已 extends Stub + 有内部 Lifecycle)。 - 本课只改一个文件:给
HelloManagerService.Lifecycle加onBootPhase方法。 - 因为 L3 改的是 L2 新增的文件,push 时它依赖 L2(Gerrit relation chain,见踩坑)。
5. 动手:给 Lifecycle 加 onBootPhase
在 L2 的 Lifecycle 类里,onStart 之后加 onBootPhase:
public static class Lifecycle extends SystemService {
private HelloManagerService mService;
public Lifecycle(Context context) { super(context); }
@Override
public void onStart() {
mService = new HelloManagerService(getContext());
publishBinderService(Context.HELLO_SERVICE, mService); // onStart 只做自给自足的事
}
/**
* onBootPhase:所有服务 onStart 完后,由 SystemServer 一轮轮广播触发。
* 数字越大越晚。在合适的 phase 做"需要别人就绪"的初始化。
*/
@Override
public void onBootPhase(int phase) {
switch (phase) {
case PHASE_SYSTEM_SERVICES_READY: // 500
// 此后可安全调 PowerManager/PackageManager
Slog.i(TAG, "onBootPhase 500 SYSTEM_SERVICES_READY");
break;
case PHASE_ACTIVITY_MANAGER_READY: // 550
// 此后可以发广播 Intent
Slog.i(TAG, "onBootPhase 550 ACTIVITY_MANAGER_READY");
break;
case PHASE_THIRD_PARTY_APPS_CAN_START: // 600
// 此后三方 App 可启动,App 能 Binder 调我
Slog.i(TAG, "onBootPhase 600 THIRD_PARTY_APPS_CAN_START");
break;
case PHASE_BOOT_COMPLETED: // 1000
// 开机完成,Home 已起
Slog.i(TAG, "onBootPhase 1000 BOOT_COMPLETED");
break;
default:
Slog.d(TAG, "onBootPhase " + phase);
break;
}
}
}
逐块讲为什么:
onStart里只new自己 +publishBinderService——这两件事不依赖任何别的服务,所以放 onStart 安全。- 假设你要在启动时读 PackageManager 拿已安装应用列表——这件事不能放 onStart,因为你的服务可能比 PMS 的某些初始化早。要放到
PHASE_SYSTEM_SERVICES_READY(500),官方保证这之后调 PMS 安全。 - 要发广播(比如通知别的组件"hello 服务好了")——必须等
PHASE_ACTIVITY_MANAGER_READY(550),因为发广播要 AMS 就绪。 - 要做开机完成后的事(读用户数据、起 UI)——等
PHASE_BOOT_COMPLETED(1000)。
6. 看底层:onBootPhase 是怎么被"广播"的
SystemServer 在所有服务 onStart 完后,会按 phase 顺序调 mSystemServiceManager.startBootPhase(phase),后者遍历所有已注册的 SystemService,挨个调它们的 onBootPhase(phase)。所以:
- 一个 phase = 一轮遍历所有服务
- phase 按 100→480→500→550→600→1000 顺序推进
- 你的服务在每一轮都会被问"这个 phase 你要做什么吗",你在 switch 里挑自己关心的 phase 处理。
7. 编译 & 运行(验证状态如实)
make services # 只改了 services 模块
# 刷机或 adb sync 后:
adb logcat | grep HelloManagerService
预期 logcat(亲眼看到顺序):
HelloManagerService: Lifecycle.onStart: 创建并上架 hello 服务
HelloManagerService: onBootPhase 500 SYSTEM_SERVICES_READY
HelloManagerService: onBootPhase 550 ACTIVITY_MANAGER_READY
HelloManagerService: onBootPhase 600 THIRD_PARTY_APPS_CAN_START
HelloManagerService: onBootPhase 1000 BOOT_COMPLETED
onStart 一定在所有 phase 之前;phase 严格按数字递增。 这就是你要亲眼确认的。
> 验证状态(诚实):代码逻辑基于真实 SystemService API;完整刷机看 logcat 需真机,未实际刷。
8. 踩坑提醒
- 依赖别人的初始化放错位置:放 onStart 里调还没起的服务 → NPE。规则:onStart 只做自给自足的,依赖别人的放对应 phase。
- 发广播放早了:onStart 或 phase<550 里
sendBroadcast→ AMS 没好,可能丢/崩。等 550。 - phase 号记混:500 是"核心服务就绪",1000 才是"开机完成"。想做开机完成后的事别用 500。
- Gerrit relation chain:L3 改的是 L2 新增的文件,L2 没合入前,L3 不能单独 cherry-pick(会冲突),要叠在含 L2 的分支上一起 push。
9. 常见问题分析与定位(启动/生命周期实战)
9.1 常见问题清单
- 服务 onStart 里 NPE,栈指向某个 getSystemService/别的服务
- 开机某阶段卡住,开不了机(boot loop / 卡 logo)
- 服务的某个初始化"有时成功有时失败"(时序竞争)
- onBootPhase 某个 phase 没被调到
- SystemServer 启动慢,开机变久
9.2 分析与定位
① onStart 里 NPE
- 现象:logcat
Failure starting core service或服务 onStart 抛 NPE,栈里有别的服务调用。 - 原因:在 onStart 里调了还没起好的服务。
- 定位:
adb logcat | grep -iE "Failure starting|NullPointer.*onStart|SystemServer"
- 修复:把那段初始化从 onStart 挪到合适的
onBootPhase(依赖 PMS/PowerManager → 500;依赖 AMS/广播 → 550)。
② 开机卡住 / boot loop
- 现象:卡 bootanimation 进不去桌面,或反复重启。
- 原因:某个服务 onStart/onBootPhase 里死循环、死锁、或抛异常导致 SystemServer 崩。SystemServer 崩 = 整个 framework 重启。
- 定位:
adb logcat -b all | grep -iE "SystemServer|watchdog|boot"
adb pull /data/anr/ # SystemServer 卡住会被 watchdog 抓 trace
adb shell cat /sys/fs/pstore/console-ramoops-0 # 若硬重启看 kernel 侧
- 看是哪个服务的哪个阶段卡住/崩溃,顺着修。
③ 时序竞争(有时成功有时失败)
- 原因:依赖关系没用 boot phase 表达,靠"运气"——有时它依赖的服务恰好先起了就成功。
- 修复:把隐式依赖改成显式——用对应 boot phase 保证顺序。
④ onBootPhase 某 phase 没调到
- 原因:switch case 写错 phase 常量;或开机没走到那个 phase(比如卡在更早阶段)。
- 定位:在 default 分支打
Slog.d(TAG, "phase "+phase),看实际收到哪些 phase。
⑤ 开机慢
- 原因:某服务 onStart/onBootPhase 里做了耗时操作(同步 IO、网络、大量计算),阻塞了整批启动。
- 定位:SystemServer 用
TimingsTraceAndSlog(t.traceBegin/traceEnd)打点,adb logcat | grep -i "SystemServerTiming"看哪个服务耗时长。 - 修复:耗时操作异步化,别阻塞启动主线程。
9.3 定位决策树
启动/生命周期出问题
├─ onStart NPE → logcat Failure starting → 调了没起的服务 → 挪到对应 onBootPhase
├─ 卡 logo/boot loop → logcat SystemServer/watchdog + /data/anr → 哪个服务哪阶段崩/卡
├─ 时好时坏 → 隐式依赖 → 用 boot phase 显式表达顺序
├─ phase 没调到 → default 打日志看实际 phase → 检查 case 常量
└─ 开机慢 → SystemServerTiming 打点 → 找耗时服务 → 异步化
10. 小结
- 三批启动 = 依赖拓扑排序:Bootstrap(最底层)→ Core → Other,顺序错就 NPE。
- boot phase 解决"启动 ≠ 就绪":所有服务 onStart 完后,按 phase 分轮广播。
- onStart 只做自给自足的事;依赖别人的初始化按 phase 推迟(调 PMS/PowerManager→500,发广播→550,开机后→1000)。
- 放错位置 = NPE / boot loop,这是 framework 新人最常见的崩溃。
- 排查口诀:onStart NPE 挪 phase;卡 logo 查 SystemServer+watchdog;时好时坏用 phase 表达依赖。
下节预告
L4:Handler / Looper / MessageQueue 消息机制 —— 主线程那个"死循环"为什么不卡死、不耗 CPU?自己手写一个 mini Looper 看透。