4、Android SystemUI启动流程:init、SystemServer、SystemUI进程启动

咱们直接切入正题。SystemUI 的启动,不是单打独斗,而是一条完整的链路。
说白了,它依赖 Android 系统从 kernel 启动后的一系列“传帮带”。
我刚开始研究这部分时,也绕晕过几次。后来发现,只要抓住 init → Zygote → SystemServer → SystemUI 这条主线,就不容易迷路。

4.1 启动链条:三阶段的接力赛

Android 系统启动,大体可以拆成三个阶段:

  • 第一阶段:init 进程 —— 内核启动后的第一个用户空间进程。它负责解析 init.rc 脚本,启动关键守护进程,比如 servicemanager、surfaceflinger,以及最重要的 Zygote。
  • 第二阶段:Zygote 进程 —— Android 的“孵化器”。它预加载了 Java 虚拟机、核心类库和系统资源。之后,Zygote 会 fork 出 SystemServer 进程。
  • 第三阶段:SystemServer 进程 —— 系统服务的“大脑”。它创建了几乎所有核心服务(AMS、WMS、PMS 等),并在服务就绪后,主动启动 SystemUI。

你想想看,SystemUI 就像是个“太子爷”,它必须等父辈们(系统服务)全部到位,才能出生。嗯,这里要注意的是,SystemUI 本身也是一个独立的进程,由 SystemServer 通过 AMS 启动。

4.2 init 进程:从这里开始

内核启动后,第一条用户空间进程是 /init。它的任务很明确:读取 /init.rc 和一系列 /init.${ro.hardware}.rc 脚本。

在这堆脚本中,我们能找到 Zygote 的启动命令:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    socket zygote stream 660 root system
    onrestart restart SurfaceFlinger
    onrestart restart audioserver

我个人习惯把 class main 看作一个关键标志——启动时属于 main 类别的服务,会在 on boot 阶段被统一拉起。
那时候我踩过一个坑:自己调试时改了脚本,不小心把 Zygote 移出了 class main,结果机器一直卡在动画循环里……后来用 logcat 追了好久才发现是服务没启动。

4.3 SystemServer 进程:服务的大本营

Zygote 启动后,会主动 fork 出 com.android.server.SystemServer。这一段是 Java 世界的入口,入口函数就叫 main()。

public static void main(String[] args) {
    new SystemServer().run();
}

在 run() 方法里,SystemServer 依次做了几件事:

  1. 加载 native 库(如 system_server、servicemanager)
  2. 创建 SystemServiceManager —— 它负责管理所有服务的生命周期
  3. 启动引导服务(Installer、DeviceIdentifiersPolicyService 等)
  4. 启动核心服务(AMS、WMS、PMS、InputManagerService 等)
  5. 启动其他服务(Telephony、Bluetooth、Wifi 等)
  6. 最后,在 startOtherServices() 中调用 startSystemUi()

你看看,顺序是固定的。一旦某个服务启动失败,后面的步骤就可能崩掉。我曾经遇到过一个 case:因为某个厂商的 sensor 服务异常,导致 InputManagerService 初始化卡住,结果整个 SystemServer 卡死,连 SystemUI 都进不去。排查了两天才定位到是 HAL 层的问题。

4.4 SystemUI 进程启动:AMS 的助攻

好了,轮到重头戏——SystemUI 的进程是如何被创建的?

代码入口在 SystemServer.java 的 startOtherServices() 里,有一行:

mActivityManagerService.systemReady(() -> {
    ...
    startSystemUi(context, windowManagerF);
    ...
});

实际上,startSystemUi() 是个静态方法:

static final void startSystemUi(Context context, WindowManagerService windowManager) {
    Intent intent = new Intent();
    intent.setComponent(new ComponentName(
        "com.android.systemui",
        "com.android.systemui.SystemUIService"));
    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
    context.startServiceAsUser(intent, UserHandle.SYSTEM);
}

说白了,就是通过 startServiceAsUser() 调用 AMS 去启动 com.android.systemui 进程里的 SystemUIService。
这里有一个关键点:SystemUI 的进程名就是 com.android.systemui,它是独立于 SystemServer 的。

如果你用 ps -A | grep systemui 查看,会看到类似:

u0_a28    1234  ...  com.android.systemui

它的 UID 是 u0_a28,属于普通应用级进程,但拥有系统级权限(因为签名为平台签名)。

那么 AMS 在启动这个 Service 时,做了什么呢?

  • 检查 AndroidManifest.xml 中 SystemUIService 的进程属性(默认就是主进程)
  • 如果该进程不存在,AMS 会 fork 新进程,并加载 com.android.systemui 的 APK
  • 初始化 Application 的 onCreate(),然后启动 Service 的 onCreate()

嗯,这里有个小技巧:SystemUI 的 Application 类(SystemUIApplication)在 onCreate() 里会做两件事:

  1. 创建所有核心依赖对象的实例(比如 Dependency 框架)
  2. 初始化各个 UI 模块(状态栏、导航栏、通知面板等)

但注意,不是所有模块都在 Application.onCreate() 里初始化,有些懒加载模块会在用到时才创建。这块我留个坑,后面讲依赖图时会展开。

4.5 关键依赖与常见避坑

根据上面的分析,我梳理了一个启动时序表,供你参考:

启动阶段 进程/组件 关键动作 前置依赖
1 init 启动 Zygote 内核、驱动
2 Zygote fork SystemServer 虚拟机、核心类库
3 SystemServer 启动 AMS、WMS 等 Binder、SurfaceFlinger
4 AMS 启动 SystemUIService SystemServer 服务齐全
5 SystemUI 初始化 UI 组件 WMS 可用、DisplayManager 可用
避坑指南(我曾经踩过的雷):
我曾经在定制低端设备时,发现 SystemUI 在开机后首次显示搜索栏时卡顿严重。查了好几天,才发现是因为 DisplayManager 服务还没完全就绪,WMS 的窗口策略初始化时序没对齐。解决方法是把 SystemUI 的某些显示初始化拆分到 onBootCompleted() 回调中,而不是所有东西都集中在 Service.onCreate() 里。
个人调试小技巧:
如果你想看 SystemUI 到底何时被启动,可以在 startSystemUi()Slog.i(TAG, "Start SystemUI"),然后抓取 logcat 过滤 SystemServer 和 SystemUIService,就可以清晰看到时间线。我曾经用这个方法对比过不同 ROM 的启动耗时差异,非常直观。

4.6 整体流程图(SVG)

下面这张图,我把从 init 到 SystemUI 进程启动的完整链路画出来了。你顺着箭头看,就是一场接力赛:

SystemUI 启动链路(init → SystemUI 进程) init 进程 解析 .rc 脚本 Zygote fork SystemServer app_process 命令启动 Zygote SystemServer 启动核心服务 AMS WMS 其他服务 SystemUI 进程 SystemUIService 使用 startServiceAsUser 启动 SystemUIService,进程独立,依赖 AMS & WMS 就绪

我个人觉得这张图最妙的地方在于,你可以一眼看到几个关键的时间点:

  • SystemServer 启动后,必须先拉起 AMS 和 WMS,才能叫醒 SystemUI
  • SystemUI 进程,是 AMS 通过 startServiceAsUser 创建的,而不是从 SystemServer 里派生

嗯,到这里,咱们把 SystemUI 的“身世”说清楚了。
下一层,我们就要深入到 SystemUI 自身内部,看看它启动后,那些依赖对象是如何被创建和串联起来的。不过今天先到这,你如果有时间,可以去 Android 源码里翻一翻 SystemServer.java 的 startOtherServices(),自己验证一下流程。