3、SystemUI的启动与初始化:SystemUIService的启动、SystemUIApplication的创建、核心组件的初始化顺序
好,咱们今天来聊聊SystemUI的启动流程。说实话,这块内容我当年刚接触时也觉得挺绕的,感觉像是一堆组件在“排队进场”。但搞清楚了之后,你会发现Android系统的UI层其实设计得非常精巧。
SystemUI的启动,说白了就是三个关键步骤:SystemUIService被系统拉起 → SystemUIApplication完成创建 → 核心组件按顺序初始化。咱们一个一个拆开看。
3.1 SystemUIService的启动:谁叫醒了我?
SystemUI本质上是一个APK,但它不是一个普通的App。它没有桌面图标,也不能被用户手动启动。那它是怎么活过来的?
答案在 AndroidManifest.xml 里。我带你看看关键配置:
<!-- SystemUI的AndroidManifest片段 -->
<application
android:name=".SystemUIApplication"
android:persistent="true"
...>
<service
android:name=".SystemUIService"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
注意看 android:persistent="true" 这个属性。嗯,这里很关键。它告诉系统:这个应用是常驻的,系统启动后必须把它拉起来。如果它挂了,系统还会自动重启它。
我记得在Android 8.0之前,SystemUI的启动还依赖 ActivityManagerService 的 systemReady() 回调。后来改成了通过 SystemServer 直接启动 SystemUIService。为什么会这样?说白了就是为了更早地拉起UI,让用户尽快看到锁屏和状态栏。
启动的入口在 SystemServer.java 里:
// SystemServer.java 中启动SystemUI
private void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
context.startServiceAsUser(intent, UserHandle.SYSTEM);
}
你看,就是通过一个显式的Intent,直接启动了SystemUIService。这里用的是 startServiceAsUser,指定了 UserHandle.SYSTEM,意味着它在系统用户空间运行,不受普通用户切换的影响。
SystemServer.startSystemUi() 里加日志。我曾经遇到过一台设备SystemUI死活起不来,最后发现是 persistent 属性被某个OTA包覆盖掉了。排查了整整两天,教训深刻。
3.2 SystemUIApplication的创建:Application的“另类”玩法
SystemUIService启动后,系统会先创建 SystemUIApplication。这个类继承自 Application,但它的 onCreate() 里做的事情,比普通App要复杂得多。
咱们直接看源码:
// SystemUIApplication.java
public class SystemUIApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 这里会初始化核心组件
startServicesIfNeeded();
}
private void startServicesIfNeeded() {
// 判断是否已经初始化过
if (mServicesStarted) {
return;
}
mServicesStarted = true;
// 准备依赖注入框架
mComponents = new SystemUIComponents();
mComponents.init(this);
// 按顺序启动核心服务
for (Class<?> clazz : CORE_SERVICES) {
Object service = mComponents.getComponent(clazz);
if (service instanceof ServiceLifecycle) {
((ServiceLifecycle) service).start();
}
}
}
}
这里有个设计模式值得注意:依赖注入。SystemUI使用了 SystemUIComponents 这个类来管理所有组件的生命周期。你想想看,如果不用依赖注入,几十个组件互相new来new去,代码会乱成什么样?
我个人习惯把 SystemUIApplication 看作是“总导演”,它不直接干活,但它知道谁该在什么时候上场。
3.3 核心组件的初始化顺序:谁先谁后?
好,重头戏来了。SystemUI的核心组件初始化顺序,是经过精心设计的。顺序错了,轻则功能异常,重则直接崩溃。
咱们看 CORE_SERVICES 这个数组的定义:
// SystemUIApplication.java 中的核心服务列表
private static final Class<?>[] CORE_SERVICES = {
// 1. 基础服务
Dependency.class, // 依赖注入容器
CommandQueue.class, // 命令队列,处理系统发来的指令
ActivityStarter.class, // Activity启动器
// 2. UI基础设施
StatusBar.class, // 状态栏
NavigationBar.class, // 导航栏
NotificationPanelView.class,// 通知面板
// 3. 功能模块
KeyguardViewMediator.class, // 锁屏
Recents.class, // 最近任务
VolumeUI.class, // 音量UI
... // 还有其他组件
};
这个顺序不是随便排的。我解释一下为什么这么排:
- Dependency 必须排第一:它是整个SystemUI的依赖注入容器。没有它,其他组件连自己的依赖都拿不到。说白了,它就是“水电煤”,必须先通上。
- CommandQueue 紧随其后:系统通过Binder向SystemUI发送指令,比如“打开通知面板”、“显示锁屏”。CommandQueue就是这些指令的“收发室”。如果它没准备好,后面的组件收到指令也没法处理。
- StatusBar 和 NavigationBar 优先于功能模块:用户最先看到的就是状态栏和导航栏。如果锁屏模块初始化慢了,至少状态栏能先显示出来,用户不会觉得手机死机了。
- 功能模块最后:锁屏、最近任务、音量UI这些,可以稍微晚一点初始化。它们依赖前面的基础设施。
KeyguardViewMediator 提到了 StatusBar 前面。结果锁屏界面显示时,状态栏还没准备好,导致锁屏上的时间显示不出来,而且下拉通知栏也拉不下来。排查了半天才发现是初始化顺序的问题。所以,千万不要随意调整CORE_SERVICES的顺序。
3.4 初始化过程中的关键细节
除了顺序,还有几个细节值得注意:
| 组件 | 初始化时机 | 关键依赖 |
|---|---|---|
| Dependency | 第一个 | 无 |
| CommandQueue | 第二个 | Dependency |
| StatusBar | 第三个 | CommandQueue, Dependency |
| KeyguardViewMediator | 靠后 | StatusBar, NotificationPanelView |
另外,每个组件的 start() 方法里,通常会做三件事:
- 注册监听器:比如监听系统设置变化、监听广播等。
- 初始化UI:如果是UI组件,会在这里创建View并添加到窗口。
- 建立连接:比如与SystemServer建立Binder连接。
这里有个小技巧:如果你需要调试某个组件的初始化过程,可以在它的 start() 方法里加 Thread.dumpStack(),看看是谁调用了它。我经常用这招来排查初始化顺序问题。
3.5 总结一下
SystemUI的启动与初始化,可以概括为:
- SystemUIService 由SystemServer通过Intent启动,
persistent属性保证它常驻内存。 - SystemUIApplication 作为Application,负责管理依赖注入和组件生命周期。
- 核心组件 按照
CORE_SERVICES数组的顺序依次初始化,顺序不可随意调整。
嗯,这一章的内容就到这里。下一章咱们会深入分析 StatusBar 的初始化过程,看看状态栏是怎么从无到有显示出来的。到时候我会分享一些我在实际项目中遇到的坑,保证让你少走弯路。