Android 车载应用开发指南系列文章
Android 车载应用开发指南(1)- 车载操作系统全解析
Android 车载应用开发指南(2)- 应用开发入门
Android 车载应用开发指南(3)- SystemUI 详解
一 SystemUI 概述
SystemUI
全称System User Interface
,直译过来就是系统级用户交互界面,在 Android 系统中由SystemUI
负责统一管理整个系统层的 UI,它是一个系统级应用程序(APK),源码在/frameworks/base/packages/
目录下。
1.1 SystemUI
Android - Phone中SystemUI
从源码量看就是一个相当复杂的程序,常见的如:状态栏、消息中心、近期任务、截屏以及一系列功能都是在SystemUI
中实现的。
源码位置:/frameworks/base/packages/SystemUI
常见 UI
组件有(包含但不限于,完整列表可以查看 SystemUI
服务组件列表)
- 状态栏
StatusBar
- 导航栏
NavigationBar
- 通知栏
NotificationPanel
- 快捷按键栏
QSPanel
- 最近任务
Recent
- 键盘锁
Keyguard
原生 Android
系统中 SystemUI
大概是这样
1.2 CarSystemUI
Android-AutoMotive
中的SystemUI
相对手机中要简单不少,目前商用车载系统中几乎必备的顶部状态栏、消息中心、底部导航栏在原生的Android
系统中都已经实现了。
源码位置:frameworks/base/packages/CarSystemUI
虽然CarSystemUI
与SystemUI
的源码位置不同,但是二者实际上是复用关系。通过阅读CarSystemUI
的 Android.bp 文件可以发现CarSystemUI
在编译时把SystemUI
以静态库的方式引入进来了。
android.bp 源码位置:/frameworks/base/packages/CarSystemUI/Android.bp
android_library {
name: "CarSystemUI-core",
...
static_libs: [
"SystemUI-core",
"SystemUIPluginLib",
"SystemUISharedLib",
"SystemUI-tags",
"SystemUI-proto",
...
],
...
}
二 SystemUI 启动流程
System UI
的启动大致可分为以下两个流程:
- 在
Framework
中启动SystemUIService
- 在
SystemUIService
中启动SystemUI
所需的各种组件
说明:本文源码分析基于版本:android-12.0.0_r3
2.1 Framework 中的流程
SystemUI
是系统应用,所以它也是一个 APK,有入口 Application
,只不过它是由 system_server
进程直接启动的。
关于SystemServer
,它是 Android framework 中关键系统的服务,由 Android 系统最核心的进程Zygote
fork 生成,进程名为system_server
。常见的ActivityManagerService
、PackageManagerService
、WindowManageService
都是由SystemServer
启动的。
SystemServer 源码路径:/frameworks/base/services/java/com/android/server/SystemServer.java
第一步:SystemServer
的 main()
方法中调用 SystemServer.run()
,run()
中调用startOtherServices()
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
... ...
// Start services.
try {
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t); //SystemServer在startOtherServices()被启动
}
... ...
}
第二步:startOtherServices()
中通过AMS
的回调方法ready()
,然后调用startSystemUi()
mActivityManagerService.systemReady(() -> {
... ...
try {
startSystemUi(context, windowManagerF);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
... ...
,t);
第三步:startSystemUi()
中可以看出,SystemUI
本质就是一个Service
,通过PM
获取到的Component
是com.android.systemui/.SystemUIService
,然后通过调用context.startServiceAsUser()
完成对SystemUIService
的启动。
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();
}
第四步:SystemUIService
依附于SystemUI
应用,所以SystemUIService
启动前需要完成SystemUI
整个应用的启动,其流程也就是应用常见的冷启动流程,这里展开讲一下:
-
SystemUI 应用启动流程
context
中的startServiceAsUser()
是一个抽象方法,具体实现在ContextImpl.java
里。实现方法startServiceCommon()
中,通过ActivityManager.getService()
就会走到AMS
中,最终在AMS
来启动SystemUIService
。
@Override
public ComponentName startServiceAsUser(Intent service, UserHandle user) {
return startServiceCommon(service, false, user);
}
@Override
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManager.getService().startService( //在AMS中开启Service
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
getOpPackageName(), getAttributionTag(), user.getIdentifier());
... ...
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
接下来进入AMS
,一探究竟:
AMS 源码路径:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
在AMS
的startService()
方法里,会经过一系列内部流程,调用到bringUpServiceLocked()
方法。
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, boolean requireForeground, String callingPackage,
String callingFeatureId, int userId)
throws TransactionTooLargeException {
... ...
try {
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, callingFeatureId, userId); // 内部调用到 startServiceLocked()
}
... ...
}
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, boolean fgRequired, String callingPackage,
@Nullable String callingFeatureId, final int userId,
boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
... ...
if (caller != null) {
// 这里记录app的进程信息
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
... ...
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); //内部调用到startServiceInnerLocked()
... ...
return cmp;
}
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
... ...
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false); //调用到bringUpServiceLocked()
if (error != null) {
return new ComponentName("!!", error);
}
... ...
return r.name;
}
继续调用了bringUpServiceLocked()
方法,
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
... ...
if (!isolated) {
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid + " app=" + app);
//如果service进程存在
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
//启动service
realStartServiceLocked(r, app, execInFg);
return null;
} catch (TransactionTooLargeException e) {
throw e;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
}
}
}
... ...
// 如果不存在此进程
if (app == null && !permissionsReviewRequired) {
// 启动运行的线程
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": process is bad";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
return msg;
}
}
... ...
return null;
}
这个方法做了两件事:
- 如果
SystemUIService
所属进程已经存在,则直接调用realStartServiceLocked()
。 - 如果
SystemUIService
所属进程不存在,则执行startProcessLocked()
方法创建进程,经过层层调用,最终也会走到realStartServiceLocked()
中:
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
... ...
try {
... ...
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.getReportedProcState());
r.postNotification();
created = true;
}
}
这个方法内部调用了app.thread.scheduleCreateService()
,而app.thread
是一个IApplicationThread
类型的,他的实现是ActivityThread
的一个内部类ApplicationThread
,而这个类正好实现了IApplicationThread.Stub
,在ApplicationThread
类中,找到对应的调用方法:
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
可以看出,是发送一个消息给Handler
,这个Handler
是ActivityThread
的内部类H
public void handleMessage(Message msg) {
switch (msg.what) {
... ...
case CREATE_SERVICE:
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
("serviceCreate: " + String.valueOf(msg.obj)));
}
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
... ...
}
}
最终调用了handleCreateService()
方法:
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
//创建service的context
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
//创建Application
Application app = packageInfo.makeApplication(false, mInstrumentation);
//获取类加载器
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//加载service实例
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
// Service resources must be initialized with the same loaders as the application
// context.
context.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
context.setOuterContext(service);
//初始化service
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
//调用service的onCreate方法
service.onCreate();
mServices.put(data.token, service);
try {
//通过serviceDoneExecuting告知AMS,service已经启动完成
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
这个方法主要做了以下几件事:
- 首先,创建上下文
- 创建
SystemUIApplication
,获取类加载器 - 加载
SystemUIService
实例,初始化SystemUIService
, 调用onCreate()
方法 - 最后通知
AMS
,SystemUIService
启动完成。
到这里SystemUIService
已经启动完成。
第五步: 前面在SystemUIApplication
创建成功后会回调内部的OnCreate()
方法,在OnCreate()
中方法注册了一个开机广播,当接收到开机广播后会调用SystemUI
的onBootCompleted()
方法来告诉每个子模块 Android 系统已经完成开机。
@Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "SystemUIApplication created.");
// 设置所有服务继承的应用程序主题。
// 请注意,在清单中设置应用程序主题仅适用于activity。这里是让Service保持与主题设置同步。
setTheme(R.style.Theme_SystemUI);
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mBootCompleteCache.isBootComplete()) return;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
unregisterReceiver(this);
mBootCompleteCache.setBootComplete();
if (mServicesStarted) {
final int N = mServices.length;
for (int i = 0; i < N; i++) {
mServices[i].onBootCompleted(); //通知SystemUI子模块
}
}
}
}, bootCompletedFilter);
...
} else {
// 我们不需要为正在执行某些任务的子进程启动服务。
...
}
}
2.2 SystemUI 中的流程
第六步:SystemUIService
初始化完成后会调用onCreate()
方法,onCreate()
中调用了SystemUIApplication
中的startServiceIfNeeded()
方法完成SystemUI
子模块的初始化。
SystemUIService 源码位置:/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
public class SystemUIService extends Service {
... ...
@Override
public void onCreate() {
super.onCreate();
// Start all of SystemUI
((SystemUIApplication) getApplication()).startServicesIfNeeded(); //调用startServicesIfNeeded()
... ...
}
}
第七步: 在SystemUIApplication
的startServicesIfNeeded()
方法中,通过SystemUIFactory
获取到配置在config.xml
中每个子模块的className
。
SystemUIApplication 源码位置:/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
// SystemUIApplication
public void startServicesIfNeeded() {
String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
startServicesIfNeeded("StartServices", names);
}
// SystemUIFactory
/** Returns the list of system UI components that should be started. */
public String[] getSystemUIServiceComponents(Resources resources) {
return resources.getStringArray(R.array.config_systemUIServiceComponents);
}
config.xml 位置:/frameworks/base/packages/SystemUI/res/values/config.xml
<!-- SystemUI Services: The classes of the stuff to start. -->
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
<item>com.android.systemui.recents.Recents</item>
<item>com.android.systemui.volume.VolumeUI</item>
<item>com.android.systemui.stackdivider.Divider</item>
<item>com.android.systemui.statusbar.phone.StatusBar</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
<item>com.android.systemui.keyboard.KeyboardUI</item>
<item>com.android.systemui.pip.PipUI</item>
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
<item>@string/config_systemUIVendorServiceComponent</item>
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.biometrics.AuthController</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.SizeCompatModeActivityController</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.theme.ThemeOverlayController</item>
<item>com.android.systemui.accessibility.WindowMagnification</item>
<item>com.android.systemui.accessibility.SystemActions</item>
<item>com.android.systemui.toast.ToastUI</item>
</string-array>
第八步: 在startServicesIfNeeded()
中通过反射完成了每个SystemUI
组件的创建,然后再调用各个SystemUI
的onStart()
方法来继续执行子模块的初始化。
private SystemUI[] mServices;
private void startServicesIfNeeded(String metricsPrefix, String[] services) {
if (mServicesStarted) {
return;
}
mServices = new SystemUI[services.length];
...
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];
if (DEBUG) Log.d(TAG, "loading: " + clsName);
try {
SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
if (obj == null) {
Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
obj = (SystemUI) constructor.newInstance(this);
}
mServices[i] = obj;
} catch (ClassNotFoundException
| NoSuchMethodException
| IllegalAccessException
| InstantiationException
| InvocationTargetException ex) {
throw new RuntimeException(ex);
}
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
// 调用各个子模块的start()
mServices[i].start();
// 首次启动时,这里始终为false,不会被调用
if (mBootCompleteCache.isBootComplete()) {
mServices[i].onBootCompleted();
}
}
mServicesStarted = true;
}
这里的SystemUI
是一个抽象类,状态栏、近期任务等等模块都是继承自SystemUI
,通过这种方式可以很大程度上简化复杂的SystemUI
程序中各个子模块创建方式,同时我们可以通过配置资源的方式动态加载需要的SystemUI
模块。
SystemUI
的源码如下,方法基本都能见名知意,就不再介绍了。
public abstract class SystemUI implements Dumpable {
protected final Context mContext;
public SystemUI(Context context) {
mContext = context;
}
public abstract void start();
protected void onConfigurationChanged(Configuration newConfig) {
}
@Override
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
}
protected void onBootCompleted() {
}
2.3 CarSystemUI 的启动流程
前文提到CarSystemUI
复用了手机SystemUI
的代码,所以CarSystemUI
的启动流程和SystemUI
的是完全一致的。
但CarSystemUI
中需要的功能与SystemUI
中也有部分差异,那么是这些差异化的功能是如何引入并完成初始化?以及一些手机的SystemUI
才需要的功能是如何去除的呢?
其实很简单,在SystemUI
的启动流程中我们得知,各个子模块的 className 是通过SystemUIFactory
的getSystemUIServiceComponents()
获取到的,那么只要继承SystemUIFactory
并重写getSystemUIServiceComponents()
就可以了。
public class CarSystemUIFactory extends SystemUIFactory {
@Override
protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
return DaggerCarSystemUIRootComponent.builder()
.contextHolder(new ContextHolder(context))
.build();
}
@Override
public String[] getSystemUIServiceComponents(Resources resources) {
Set<String> names = new HashSet<>();
// 先引入systemUI中的components
for (String s : super.getSystemUIServiceComponents(resources)) {
names.add(s);
}
// 再移除CarsystemUI不需要的components
for (String s : resources.getStringArray(R.array.config_systemUIServiceComponentsExclude)) {
names.remove(s);
}
// 最后再添加CarsystemUI特有的components
for (String s : resources.getStringArray(R.array.config_systemUIServiceComponentsInclude)) {
names.add(s);
}
String[] finalNames = new String[names.size()];
names.toArray(finalNames);
return finalNames;
}
}
<!-- 需要移除的Components. -->
<string-array name="config_systemUIServiceComponentsExclude" translatable="false">
<item>com.android.systemui.recents.Recents</item>
<item>com.android.systemui.volume.VolumeUI</item>
<item>com.android.systemui.stackdivider.Divider</item>
<item>com.android.systemui.statusbar.phone.StatusBar</item>
<item>com.android.systemui.keyboard.KeyboardUI</item>
<item>com.android.systemui.pip.PipUI</item>
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.accessibility.WindowMagnification</item>
<item>com.android.systemui.accessibility.SystemActions</item>
</string-array>
<!-- 新增的Components. -->
<string-array name="config_systemUIServiceComponentsInclude" translatable="false">
<item>com.android.systemui.car.navigationbar.CarNavigationBar</item>
<item>com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier</item>
<item>com.android.systemui.car.window.SystemUIOverlayWindowManager</item>
<item>com.android.systemui.car.volume.VolumeUI</item>
</string-array>
通过以上方式,就完成了CarSystemUI
子模块的替换。
2.4 小结
总结一下,SystemUI
的大致启动流程可以归纳如下:
SystemUI
是一个 persistent
应用,它由操作系统启动,主要流程为
-
Android
系统在开机后会创建system_server
进程,它会启动各种系统所需要的服务,其中就包括SystemUIService
。 -
SystemUIService
启动后进入到应用层SystemUI
中,在SystemUIApplication
它首先会初始化监听ACTION_BOOT_COMPLETED
等通知,待系统完成启动后会通知各个组件onBootCompleted
。 - 在进入
SystemUIService
中依然执行的SystemUIApplication
中的startServicesIfNeeded()
方法启动SystemUI
中的子模块。 - 最终的服务启动逻辑都是在
SystemUIApplication
里面,并且都保存在mServices
数组中。
三 总结
SystemUI
在原生的车载 Android 系统是一个较为复杂的模块,本文主要介绍了SystemUI
和CarSystemUI
的功能、源码结构及启动时序,希望能帮到从事SystemUI
开发的同学。文章来源:https://www.toymoban.com/news/detail-825837.html
四 参考文档
Android 车载应用开发与分析(12) - SystemUI (一)文章来源地址https://www.toymoban.com/news/detail-825837.html
到了这里,关于Android 车载应用开发指南(3) - SystemUI 详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!