在 Andorid 系统源码中,package/apps下放的是系统内置的一些 APP,例如 Settings、Camera、Phone、Message 等等。而在 framework/base/package 下,它们也是系统的 APP,SystemUI 就在此目录下。它控制着整个 Android 系统的界面,但其实他也是一个 APP,不同于一般的 APP,它不可卸载也不可以被第三方应用替换。对于用户而言,SystemUI 的改动是最能直观感受到的。因此,每个 Android 版本在 SystemUI 上都有比较大的改动。而对开发者而言,理解 Android SystemUI 对优化 Android 系统界面,改善用户体验十分重要。
一、启动流程
因为 SystemUI 是系统应用,所以它也是一个 APK,有入口 Application,只不过它是由 SystemServer 进程进行启动的。
1、SystemServer
在 Android 系统之后,系统首先会启动一个名为 Zygote 的进程,而 Zygote 进程又会启动 SystemServer 进程,SystemServer 又会启动 SystemUI,这里我们先来看 SystemServer 的 main() 方法。
源码位置:/frameworks/base/services/java/com/android/server/SystemServer.java
main
java
public final class SystemServer implements Dumpable {
......
public static void main(String[] args) {
new SystemServer().run();
}
}
这里启动了 run() 方法。
run
java
private void run() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
......
// Start services.
try {
t.traceBegin("StartServices");
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t);
} catch (Throwable ex) {
......
throw ex;
} finally {
t.traceEnd(); // StartServices
}
......
}
在 run 方法中调用了startOtherServices() 方法。
startOtherServices
java
private void startOtherServices() {
......
mActivityManagerService.systemReady(() -> {
......
try {
startSystemUi(context, windowManagerF);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
......
}
}
在 startOtherServices() 方法里面,mActivityManagerService 的 systemReady 回调方法中会创建线程去执行 startSystemUi() 方法。
startSystemUi
java
private static void startSystemUi(Context context, WindowManagerService windowManager) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
Intent intent = new Intent();
intent.setComponent(pm.getSystemUiServiceComponent());
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
windowManager.onSystemUiStarted();
}
可以看到 startSystemUi() 方法首先获取 PackageManagerInternal 对象实例 pm,再调用 pm 的 getSystemUiServiceComponent() 方法获取 SystemUIService 组件的路径,最后再调用 startServiceAsUser() 方法启动 SystemUIService 服务。
二、启动信息详解
这里主要通过使用 Context 和 WindowManagerService 来初始化并启动 SystemUI。
1、获取服务信息
java
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
pm.getSystemUiServiceComponent();
这里使用 LocalServices.getService() 方法来获取 PackageManagerInternal 的单例实例。然后通过 PackageManagerInternal 中的 getSystemUiServiceComponent() 方法获取 SystemUI 的包名和类名。
PackageManagerInternal
源码位置:/frameworks/base/services/core/java/android/content/pm/PackageManagerInternal.java
java
public abstract class PackageManagerInternal implements PackageSettingsSnapshotProvider {
......
/**
* 返回 SystemUI 服务组件名
*/
public abstract ComponentName getSystemUiServiceComponent();
}
PackageManagerInternal 是一个抽象类,同样 getSystemUiServiceComponent() 也是抽象方法,该方法在 PackageManagerService 的内部类 PackageManagerInternalImpl 中进行了具体实现。
PackageManagerService
源码位置:/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
java
private class PackageManagerInternalImpl extends PackageManagerInternal {
......
public ComponentName getSystemUiServiceComponent() {
return ComponentName.unflattenFromString(mContext.getResources().getString(
com.android.internal.R.string.config_systemUIServiceComponent));
}
......
}
可以看到 ComonentName 是从一个内部资源字符串com.android.internal.R.string.config_systemUIServiceComponent 获取 SystemUIService 组件完整类名的。
config.xml
源码位置:/frameworks/base/core/res/res/values/config.xml
XML
<!-- SystemUi service component -->
<string name="config_systemUIServiceComponent" translatable="false"
>com.android.systemui/com.android.systemui.SystemUIService</string>
可以发现 config_systemUIServiceComponent 这个资源字符串的具体位置和内容如上所示。
2、创建 Intent 对象
java
Intent intent = new Intent();
intent.setComponent(pm.getSystemUiServiceComponent());
intent.addFlags(Intent.FLAG_DEBUG_TRIADED_MISSING);
- 创建一个新的 Intent 对象,用于描述要启动的服务。
- 通过 setComponent() 指定了要启动的服务(即 SystemUI)的包名和类名时。
- 通过 addFlags() 设置相关标识,FLAG_DEBUG_TRIAGED_MISSING 标志,这个标志通常用于调试目的,可以帮助追踪某些类型的错误或问题。
3、启动服务
java
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
- 注释掉的日志记录语句原本可以用来输出调试信息。
- 使用 context.startServiceAsUser() 方法以 UserHandle.SYSTEM 用户身份启动服务。这意味着服务将以系统的名义运行,拥有更高的权限。
Context
源码位置:/frameworks/base/core/java/android/content/Context.java
java
public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);
这里调用了 startServiceAsUser() 方法来启动服务,而该方法的实现是在 ContextImpl 中实现。
ContextImpl
源码位置:/frameworks/base/core/java/android/app/ContextImpl.java
java
public ComponentName startServiceAsUser(Intent service, UserHandle user) {
return startServiceCommon(service, false, user);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground, UserHandle user) {
try {
// 验证服务意图是否有效
validateServiceIntent(service);
// 准备将 Intent 发送到另一个进程
service.prepareToLeaveProcess(this);
// 实际启动服务
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
getOpPackageName(), getAttributionTag(), user.getIdentifier());
......
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
- mMainThread.getApplicationThread() 提供了当前应用线程的信息。
- service.resolveTypeIfNeeded(getContentResolver()) 解析并确定服务的 MIME 类型(如果需要)。
- requireForeground 决定服务是否应该以前台服务的形式启动。
- getOpPackageName() 和 getAttributionTag() 分别提供操作包名和归属标签,用于追踪和记录目的。
- user.getIdentifier() 指定服务启动时使用的用户 ID。
真正的启动服务涉及到与 ActivityManagerService 的通信,后者负责管理所有应用程序的生命周期和状态。具体逻辑这里就不做过多介绍了,其中《AMS》专栏已经做了详细介绍。
4、通知WMS
java
windowManager.onSystemUiStarted();
调用 windowManager.onSystemUiStarted() 方法,通知 WindowManagerService SystemUI 已经开始启动。这一步对于确保窗口管理器和其他系统组件知道 SystemUI 的状态非常重要。
WindowManagerService
源码位置:/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
java
WindowManagerPolicy mPolicy;
public void onSystemUiStarted() {
mPolicy.onSystemUiStarted();
}
WindowManagerPolicy 同样是一个接口类,具体实现是在 PhoneWindowManager 中。
PhoneWindowManager
源码位置:/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
java
@Override
public void onSystemUiStarted() {
bindKeyguard();
}
private void bindKeyguard() {
synchronized (mLock) {
if (mKeyguardBound) {
return;
}
mKeyguardBound = true;
}
mKeyguardDelegate.bindService(mContext);
}
这里主要功能是绑定锁屏(Keyguard)服务,为了确保 Android 设备能够在启动后立即提供安全可靠的锁屏功能,并与其他系统组件无缝协作,为用户提供一致且流畅的交互体验。