AI 技术博客
Android全栈11 分钟阅读6615

【全栈第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 用两个机制解决:

  1. 分批 + 固定顺序启动(解决"谁先谁后")
  2. 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.LifecycleonBootPhase 方法。
  • 因为 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. 踩坑提醒

  1. 依赖别人的初始化放错位置:放 onStart 里调还没起的服务 → NPE。规则:onStart 只做自给自足的,依赖别人的放对应 phase。
  2. 发广播放早了:onStart 或 phase<550 里 sendBroadcast → AMS 没好,可能丢/崩。等 550。
  3. phase 号记混:500 是"核心服务就绪",1000 才是"开机完成"。想做开机完成后的事别用 500。
  4. Gerrit relation chain:L3 改的是 L2 新增的文件,L2 没合入前,L3 不能单独 cherry-pick(会冲突),要叠在含 L2 的分支上一起 push。

9. 常见问题分析与定位(启动/生命周期实战)

9.1 常见问题清单

  1. 服务 onStart 里 NPE,栈指向某个 getSystemService/别的服务
  2. 开机某阶段卡住,开不了机(boot loop / 卡 logo)
  3. 服务的某个初始化"有时成功有时失败"(时序竞争)
  4. onBootPhase 某个 phase 没被调到
  5. 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. 小结

  1. 三批启动 = 依赖拓扑排序:Bootstrap(最底层)→ Core → Other,顺序错就 NPE。
  2. boot phase 解决"启动 ≠ 就绪":所有服务 onStart 完后,按 phase 分轮广播。
  3. onStart 只做自给自足的事;依赖别人的初始化按 phase 推迟(调 PMS/PowerManager→500,发广播→550,开机后→1000)。
  4. 放错位置 = NPE / boot loop,这是 framework 新人最常见的崩溃。
  5. 排查口诀:onStart NPE 挪 phase;卡 logo 查 SystemServer+watchdog;时好时坏用 phase 表达依赖。

下节预告

L4:Handler / Looper / MessageQueue 消息机制 —— 主线程那个"死循环"为什么不卡死、不耗 CPU?自己手写一个 mini Looper 看透。

评论