首语
SystemUI(System User Interface)是Android 系统为用户提供的系统级别的信息显示与交互的UI组件应用程序,包含状态栏、导航栏、锁屏、通知面板、快速设置、最近任务等,它们各部分独立,各尽其责。
SystemUI是一个常驻应用程序,只要系统运行,它就会一直运行,即使被杀死也会重新启动。
源码目录
SystemUI源码目录在frameworks/base/package下,和Settings不一样,它不在package/apps下。
从Android.bp文件中可以得出,模块名为SystemUI。
架构
SystemUI各组件经常会互相交互,例如点击通知面板通知在锁屏情况下会跳转锁屏页面先解锁,解锁情况下跳转锁屏内容页面,因此通知面板需要清楚锁屏状态来进行处理。为此需要设计好架构,还轻松获取到各个组件,避免产生很多的创建代码,SystemUI使用了依赖注入自动创建对象,SystemUI使用Dagger2来作为依赖注入库,管理组件。
Android系统中,为了适应多元化场景,诸如TV、Car,它们又是不同的UI组件,同时满足模块化设计,因此将SytemUI独立成一个单独的、常驻内存的应用程序。
SystemUI各种组件UI均存在UIController来进行UI逻辑的处理和交互,不同组件源码通过目录清晰划分。
关于Dagger2,可参考以下文档:
Android 官方:https://developer.android.google.cn/training/dependency-injection/dagger-basics?hl=zh_cn
Dagger: https://dagger.dev/dev-guide
AndroidManifest
由Manifest可以看出,SystemUI是一个比较特殊的应用程序,有许多特定设置项。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
package="com.android.systemui"
<!--指定共享的uid-->
android:sharedUserId="android.uid.systemui"
xmlns:tools="http://schemas.android.com/tools"
coreApp="true">
<application
android:name=".SystemUIApplication"
android:persistent="true"
<!--不允许清除应用数据-->
android:allowClearUserData="false"
android:backupAgent=".backup.BackupHelper"
<!--应用程序被恢复后继续运行-->
android:killAfterRestore="false"
<!--启用硬件加速-->
android:hardwareAccelerated="true"
android:label="@string/app_label"
android:icon="@drawable/icon"
<!--指定进程-->
android:process="com.android.systemui"
android:supportsRtl="true"
android:theme="@style/Theme.SystemUI"
<!--默认存储空间重定向到data/user_de-->
android:defaultToDeviceProtectedStorage="true"
<!--DirectBoot 模式下可以启动-->
android:directBootAware="true"
tools:replace="android:appComponentFactory"
<!--指定应用程序组件工厂,动态创建和初始化组件-->
android:appComponentFactory=".SystemUIAppComponentFactory">
</application>
</manifest>
启动流程
在SystemServer 进程启动过程文章中,我们清楚SystemServer进程主要工作是启动系统服务,其中在startOtherServices方法中,当AMS系统服务进入就绪状态时,会调用startSystemUi方法来启动SystemUI,其中AMS是在startBootstrapServices方法中被启动的。在startSystemUi方法中启动了一个服务,服务intent的component通过PackageManagerInternal类的getSystemUiServiceComponent方法获取,PackageManagerInternal是一个抽象类,它的实现在PackageManagerInternalBase类中,可以看到getSystemUiServiceComponent实现中返回了config_systemUIServiceComponent字符串。
源码路径:
frameworks\base\services\java\com\android\server\SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
mActivityManagerService.systemReady(() -> {
...
t.traceBegin("StartSystemUI");
try {
startSystemUi(context, windowManagerF);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
t.traceEnd();
},t);
}
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();
}
源码路径:frameworks\base\services\core\java\com\android\server\pm\PackageManagerInternalBase.java
@Override
@Deprecated
public final ComponentName getSystemUiServiceComponent() {
return ComponentName.unflattenFromString(getContext().getResources().getString(
com.android.internal.R.string.config_systemUIServiceComponent));
}
可以看到指定的Component包名为com.android.systemui,类名为com.android.systemui.SystemUIService。
到这里我们就清楚,SystemUI是SystemServer进程启动SystemUIService来启动的。
源码路径:frameworks\base\core\res\res\values\config.xml
<!-- SystemUi service component -->
<string name="config_systemUIServiceComponent" translatable="false"
>com.android.systemui/com.android.systemui.SystemUIService</string>
SystemUIService类中调用SystemUIApplication类的startServicesIfNeeded方法。
源码路径:
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIService.java
@Override
public void onCreate() {
super.onCreate();
// Start all of SystemUI
((SystemUIApplication) getApplication()).startServicesIfNeeded();
...
}
首先获取定制的系统UI组件,其为VenderService。继续通过getStartableComponents和getStartableComponentsPerUser方法获取组件
源码路径
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIApplication.java
public void startServicesIfNeeded() {
//定制系统UI组件
final String vendorComponent = SystemUIFactory.getInstance()
.getVendorComponent(getResources());
Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>(
Comparator.comparing(Class::getName));
sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponents());
sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser());
startServicesIfNeeded(
sortedStartables, "StartServices", vendorComponent);
}
源码路径:
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIFactory.java
// <string name="config_systemUIVendorServiceComponent" translatable="false">com.android.systemui.VendorServices</string>
public String getVendorComponent(Resources resources) {
return resources.getString(R.string.config_systemUIVendorServiceComponent);
}
public Map<Class<?>, Provider<CoreStartable>> getStartableComponents() {
return mSysUIComponent.getStartables();
}
public Map<Class<?>, Provider<CoreStartable>> getStartableComponentsPerUser() {
return mSysUIComponent.getPerUserStartables();
}
这里使用了Dagger2的@IntoMap注入相关类,只要是继承CoreStartable类的都将会被注入。
源码路径:
frameworks\base\packages\SystemUI\src\com\android\systemui\dagger\SysUIComponent.java
@SysUISingleton
@Subcomponent(modules = {
DefaultComponentBinder.class,
DependencyProvider.class,
SystemUIBinder.class,
SystemUIModule.class,
SystemUICoreStartableModule.class,
ReferenceSystemUIModule.class})
public interface SysUIComponent {
Map<Class<?>, Provider<CoreStartable>> getStartables();
@PerUser Map<Class<?>, Provider<CoreStartable>> getPerUserStartables();
}
注入方法如下,相关注入结束后,继续回到SystemUIApplication类的startServicesIfNeeded方法。
源码路径:
frameworks\base\packages\SystemUI\src\com\android\systemui\dagger\SystemUICoreStartableModule.kt
@Module
abstract class SystemUICoreStartableModule {
/** Inject into AuthController. */
@Binds
@IntoMap
@ClassKey(AuthController::class)
abstract fun bindAuthController(service: AuthController): CoreStartable
/** Inject into ClipboardListener. */
@Binds
@IntoMap
@ClassKey(ClipboardListener::class)
abstract fun bindClipboardListener(sysui: ClipboardListener): CoreStartable
......
}
获取所有组件后,启动所有组件,至此System UI和SystemUI所有组件启动完成。
private void startServicesIfNeeded(
Map<Class<?>, Provider<CoreStartable>> startables,
String metricsPrefix,
String vendorComponent) {
if (mServicesStarted) {
return;
}
mServices = new CoreStartable[startables.size() + (vendorComponent == null ? 0 : 1)];
if (!mBootCompleteCache.isBootComplete()) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleteCache.setBootComplete();
if (DEBUG) {
Log.v(TAG, "BOOT_COMPLETED was already sent");
}
}
}
mDumpManager = mSysUIComponent.createDumpManager();
Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
log.traceBegin(metricsPrefix);
int i = 0;
for (Map.Entry<Class<?>, Provider<CoreStartable>> entry : startables.entrySet()) {
String clsName = entry.getKey().getName();
int j = i; // Copied to make lambda happy.
timeInitialization(
clsName,
//启动组件
() -> mServices[j] = startStartable(clsName, entry.getValue()),
log,
metricsPrefix);
i++;
}
if (vendorComponent != null) {
timeInitialization(
vendorComponent,
() -> mServices[mServices.length - 1] =
startAdditionalStartable(vendorComponent),
log,
metricsPrefix);
}
for (i = 0; i < mServices.length; i++) {
if (mBootCompleteCache.isBootComplete()) {
mServices[i].onBootCompleted();
}
mDumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);
}
mSysUIComponent.getInitController().executePostInitTasks();
log.traceEnd();
mServicesStarted = true;
}
private CoreStartable startStartable(String clsName, Provider<CoreStartable> provider) {
if (DEBUG) Log.d(TAG, "loading: " + clsName);
return startStartable(provider.get());
}
private CoreStartable startStartable(CoreStartable startable) {
if (DEBUG) Log.d(TAG, "running: " + startable);
startable.start();
return startable;
}
总结
SystemUI是Android系统中与用户交互频繁的UI组件应用程序,地位不言而喻。后续会对SystemUI中重要的组件进行单独分析,深入了解。同时对于这个一个UI组件应用程序,Android 14引入了Jetpack Compse进行部分UI代码的重构,在保持稳定性的前提下官方会持续进行重构,到时架构及实现将会发生重大变化。
官方文档:frameworks/base/package/SystemUI/docs/