系列导航 :一:全景概览与架构设计 | 二:启动流程深度解析(本文) | 三:StatusBar 状态栏 | 四:NavigationBar 导航栏 | 五:通知系统 | 六:QuickSettings | 七:Keyguard 锁屏 | 八:Recents 与其他子系统
一、概述
SystemUI 是系统启动过程中第一个用户肉眼可见的应用------开机后看到的锁屏界面、状态栏、导航栏都由它负责。理解 SystemUI 的启动流程,是深入掌握其内部机制的第一步。
整个启动链路可以概括为三个阶段:
阶段一:SystemServer 启动 SystemUIService
→ 阶段二:SystemUIService 初始化 SystemUIApplication
→ 阶段三:SystemUIApplication 按 SERVICES 数组逐个加载子模块
本文将以 AOSP7 源码为基准,逐层追踪这一完整链路。
二、阶段一:SystemServer 中的启动入口
2.1 SystemServer 的 run() 方法
SystemServer 是 Android 系统的核心进程,负责启动所有 Java 层系统服务。run() 方法按阶段依次启动各类服务,SystemUI 的启动入口就在其中的 startOtherServices() 里。
源码路径 :frameworks/base/services/java/com/android/server/SystemServer.java
java
public final class SystemServer {
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
// ... 初始化 Looper、加载本地库 ...
// 创建系统 Context
createSystemContext();
// 启动引导服务
startBootstrapServices();
// 启动核心服务(AMS、WMS、PMS 等)
startCoreServices();
// 启动其他服务:SystemUI 正是在这里、在 AMS 的 systemReady 回调中被启动
startOtherServices();
// 进入消息循环
Looper.loop();
}
}
关键设计 :
startOtherServices()只是把所有服务"启动起来",SystemUI 并不是在这里直接拉起,而是等到 AMS 发出systemReady回调时才真正启动,以保证 AMS / WMS 等核心服务已就绪。
2.2 startOtherServices() 中的 SystemUI 启动
SystemUI 的启动发生在 startOtherServices() 方法中,在 AMS 的 systemReady() 回调里执行。回调内部用 try/catch 包裹,并用 Trace 标记出这段耗时,启动 SystemUI 的调用就藏在这段 run() 里。
源码路径 :frameworks/base/services/java/com/android/server/SystemServer.java
java
public final class SystemServer {
// ...
private void startOtherServices() {
// ... 启动大量系统服务 ...
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready");
// ... 进入 PHASE_ACTIVITY_MANAGER_READY、启动各项 systemReady ...
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartSystemUI");
try {
startSystemUi(context); // 启动 SystemUI
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
// ... 继续把其它服务置为 ready ...
}
});
}
}
2.3 startSystemUi() 方法详解
startSystemUi() 是一个 static final 方法,它构造一个显式 Intent 指定 SystemUIService 组件,再以 SYSTEM 用户身份启动它。
源码路径 :frameworks/base/services/java/com/android/server/SystemServer.java
java
public final class SystemServer {
// ...
static final void startSystemUi(Context context) {
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以UserHandle.SYSTEM身份、显式指定com.android.systemui.SystemUIService组件来启动 SystemUI。SystemUI 本身是Service而非Activity,运行在独立进程里(由AndroidManifest.xml的android:process决定)。
三、阶段二:SystemUIService 的初始化
3.1 SystemUIService 的 onCreate()
SystemUIService 本身几乎没有逻辑,onCreate() 只做一件事:把真正的启动逻辑委托给 SystemUIApplication 的 startServicesIfNeeded()。
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
java
public class SystemUIService extends Service {
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
关键设计 :
SystemUIService没有重写onStartCommand(),启动动作全部发生在onCreate()中------它把this强转为SystemUIApplication并调用startServicesIfNeeded(),真正的子模块加载逻辑都在 Application 那一层。
3.2 SystemUI 的 AndroidManifest 配置
xml
<!-- frameworks/base/packages/SystemUI/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.systemui"
coreApp="true"
android:sharedUserId="android.uid.system"> <!-- ★ 系统 UID -->
<application
android:name=".SystemUIApplication"
android:persistent="true" <!-- ★ 持续运行 -->
android:process="com.android.systemui" <!-- ★ 独立进程 -->
...>
<service
android:name="SystemUIService"
android:exported="true"
.../>
</application>
</manifest>
下面三个属性决定了 SystemUI 能以系统身份、独立进程、常驻方式运行:
| 属性 | 值 | 含义 |
|---|---|---|
android:sharedUserId |
android.uid.system |
系统级权限 |
android:persistent |
true |
进程被杀后自动重启 |
android:process |
com.android.systemui |
独立的进程空间 |
四、阶段三:SystemUIApplication 加载子模块
4.1 startServicesIfNeeded() 方法
startServicesIfNeeded() 是启动子模块的核心循环:遍历组件数组,逐个实例化并注入上下文,然后调用每个模块的 start()。注意它并不从资源里读字符串数组,而是遍历一个硬编码的 Class<?>[]。
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
java
public class SystemUIApplication extends Application {
private final Class<?>[] SERVICES = new Class[] { /* 见 4.2 节 */ };
private final SystemUI[] mServices = new SystemUI[SERVICES.length];
private boolean mServicesStarted;
private boolean mBootCompleted;
private final Map<Class<?>, Object> mComponents = new HashMap<>();
public void startServicesIfNeeded() {
startServicesIfNeeded(SERVICES);
}
private void startServicesIfNeeded(Class<?>[] services) {
if (mServicesStarted) {
return;
}
// ... 检查 sys.boot_completed 判断是否已经开机完成 ...
final int N = services.length;
for (int i = 0; i < N; i++) {
Class<?> cl = services[i];
try {
Object newService = SystemUIFactory.getInstance().createInstance(cl);
mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
mServices[i].start();
if (mBootCompleted) {
mServices[i].onBootCompleted();
}
}
mServicesStarted = true;
}
}
关键设计 :实例化优先走
SystemUIFactory.getInstance().createInstance(cl),只有当工厂返回null时才回退到cl.newInstance(),并非纯反射Class.forName(name)。启动顺序即数组顺序,且若系统已BOOT_COMPLETED会顺带调用onBootCompleted()。
4.2 硬编码的 SERVICES 组件数组
SystemUI 的"启动清单"并不是资源里的字符串数组,而是一个硬编码在 SystemUIApplication 里的 Class<?>[] SERVICES,数组顺序即启动顺序:
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
java
public class SystemUIApplication extends Application {
private final Class<?>[] SERVICES = new Class[] {
com.android.systemui.tuner.TunerService.class,
com.android.systemui.keyguard.KeyguardViewMediator.class,
com.android.systemui.recents.Recents.class,
com.android.systemui.volume.VolumeUI.class,
Divider.class,
com.android.systemui.statusbar.SystemBars.class,
com.android.systemui.usb.StorageNotification.class,
com.android.systemui.power.PowerUI.class,
com.android.systemui.media.RingtonePlayer.class,
com.android.systemui.keyboard.KeyboardUI.class,
com.android.systemui.tv.pip.PipUI.class,
com.android.systemui.shortcut.ShortcutKeyDispatcher.class,
com.android.systemui.VendorServices.class
};
}
关键设计 :调整子模块的启动顺序,需要直接改这个
Class<?>[] SERVICES数组,而SERVICES.length也决定了mServices数组的大小。
4.3 SystemUI 基类
所有子模块都继承自 SystemUI 基类。它声明了 mContext 和 mComponents 两个共享字段,以及抽象的 start() 和若干可选回调,startServicesIfNeeded() 循环里注入的正是这两个字段。
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUI.java
java
public abstract class SystemUI {
public Context mContext;
public Map<Class<?>, Object> mComponents;
public abstract void start();
protected void onConfigurationChanged(Configuration newConfig) {
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
}
protected void onBootCompleted() {
}
// ... getComponent / putComponent 依赖注入辅助方法 ...
}
关键设计 :这是一个典型的模板方法模式 ------基类定义字段与生命周期框架,子类只需实现
start()完成各自初始化;mComponents是模块间共享的依赖容器,onBootCompleted()则在系统完成开机后统一回调。
五、启动时序图
SystemServer SystemUIService SystemUIApplication 各子模块 (SERVICES[i])
| | | |
|--startSystemUi()-------->| | |
| (startServiceAsUser) | | |
| |--onCreate()---------->| |
| | | |
| | startServicesIfNeeded() |
| | | |
| | 遍历 Class<?>[] SERVICES: |
| | | |
| | SystemUIFactory.createInstance / cl.newInstance
| | | |
| | mServices[i].start()------------->|
| | | |
| | | TunerService |
| | | KeyguardViewMediator|
| | | Recents |
| | | SystemBars |
| | | PowerUI ... |
| | | |
| |<-----启动完成-----------| |
| | | |
六、启动顺序的重要影响
6.1 为什么 TunerService 和 KeyguardViewMediator 排在前面?
TunerService 是 SERVICES 数组的第一个元素,负责调参/调试相关的早期初始化;紧随其后的 KeyguardViewMediator 负责锁屏窗口,是开机后最先需要呈现给用户界面的模块之一,因此需要在更靠后的 SystemBars 之前就位。
6.2 为什么 SystemBars 在中间?
SystemBars 负责创建状态栏和导航栏窗口,它排在 KeyguardViewMediator、Recents、VolumeUI、Divider 之后,等这些依赖基础窗口的模块就绪后再构建状态栏/导航栏窗口,避免早期模块引用到尚未创建的状态栏。
6.3 为什么 VendorServices 在最后?
VendorServices 是设备厂商的扩展入口,放在最后确保所有 AOSP 标准模块都已初始化完成。
七、启动流程中的关键日志
在调试 SystemUI 启动问题时,可以关注以下日志标签:
bash
# 查看 SystemServer 启动 SystemUI 的日志
adb logcat | grep "SystemServer"
# 查看 SystemUI 各模块启动日志
adb logcat | grep "SystemUIService"
adb logcat | grep "SystemUIApplication"
# 查看具体模块的启动状态
adb logcat | grep "PhoneStatusBar"
adb logcat | grep "KeyguardViewMediator"
adb logcat | grep "SystemBars"
八、小结
本文梳理了 SystemUI 从 SystemServer 到各个子模块的完整启动链路:
- SystemServer.startSystemUi() → 以 Service 形式启动
SystemUIService - SystemUIService.onCreate() → 委托给
SystemUIApplication.startServicesIfNeeded() - SystemUIApplication → 遍历硬编码的
Class<?>[] SERVICES,通过SystemUIFactory(回退newInstance())逐一实例化并调用start()
关键设计思想:
- 统一入口 :所有子模块由
SystemUIApplication按SERVICES数组顺序统一拉起 - 统一基类 :所有子模块继承
SystemUI,统一生命周期管理 - 依赖注入 :
mComponents容器 +SystemUIFactory管理模块间的依赖关系 - 独立进程:SystemUI 拥有独立进程空间,与系统服务通过 Binder 通信
下一篇我们将深入 SystemUI 的核心枢纽------StatusBar 状态栏 ,从 PhoneStatusBar 的 makeStatusBarView() 开始,逐一拆解状态栏窗口的创建、图标管理和下拉手势处理。
下一篇:AOSP7 SystemUI 源码解析(三):StatusBar 状态栏源码分析