車(chē)載系統(tǒng) Android 車(chē)載應(yīng)用開(kāi)發(fā)指南(3) - SystemUI 詳解
Android 車(chē)載應(yīng)用開(kāi)發(fā)指南系列文章
Android 車(chē)載應(yīng)用開(kāi)發(fā)指南(1)- 車(chē)載操作系統(tǒng)全解析
Android 車(chē)載應(yīng)用開(kāi)發(fā)指南(2)- 應(yīng)用開(kāi)發(fā)入門(mén)
Android 車(chē)載應(yīng)用開(kāi)發(fā)指南(3)- SystemUI 詳解
一 SystemUI 概述
SystemUI全稱(chēng)System User Interface,直譯過(guò)來(lái)就是系統(tǒng)級(jí)用戶(hù)交互界面,在 Android 系統(tǒng)中由SystemUI負(fù)責(zé)統(tǒng)一管理整個(gè)系統(tǒng)層的 UI,它是一個(gè)系統(tǒng)級(jí)應(yīng)用程序(APK),源碼在/frameworks/base/packages/目錄下。
1.1 SystemUI
Android - Phone中SystemUI從源碼量看就是一個(gè)相當(dāng)復(fù)雜的程序,常見(jiàn)的如:狀態(tài)欄、消息中心、近期任務(wù)、截屏以及一系列功能都是在SystemUI中實(shí)現(xiàn)的。
源碼位置:/frameworks/base/packages/SystemUI
常見(jiàn) UI 組件有(包含但不限于,完整列表可以查看 SystemUI 服務(wù)組件列表)
狀態(tài)欄 StatusBar導(dǎo)航欄 NavigationBar通知欄 NotificationPanel快捷按鍵欄 QSPanel最近任務(wù) Recent鍵盤(pán)鎖 Keyguard
原生 Android 系統(tǒng)中 SystemUI 大概是這樣
1.2 CarSystemUI
Android-AutoMotive中的SystemUI相對(duì)手機(jī)中要簡(jiǎn)單不少,目前商用車(chē)載系統(tǒng)中幾乎必備的頂部狀態(tài)欄、消息中心、底部導(dǎo)航欄在原生的Android系統(tǒng)中都已經(jīng)實(shí)現(xiàn)了。
源碼位置:frameworks/base/packages/CarSystemUI
雖然CarSystemUI與SystemUI的源碼位置不同,但是二者實(shí)際上是復(fù)用關(guān)系。通過(guò)閱讀CarSystemUI的 Android.bp 文件可以發(fā)現(xiàn)CarSystemUI在編譯時(shí)把SystemUI以靜態(tài)庫(kù)的方式引入進(jìn)來(lái)了。
android.bp 源碼位置:/frameworks/base/packages/CarSystemUI/Android.bp
android_library {
name: "CarSystemUI-core",
...
static_libs: [
"SystemUI-core",
"SystemUIPluginLib",
"SystemUISharedLib",
"SystemUI-tags",
"SystemUI-proto",
...
],
...
}
二 SystemUI 啟動(dòng)流程
System UI的啟動(dòng)大致可分為以下兩個(gè)流程:
在Framework中啟動(dòng)SystemUIService在SystemUIService中啟動(dòng)SystemUI所需的各種組件
說(shuō)明:本文源碼分析基于版本:android-12.0.0_r3
2.1 Framework 中的流程
SystemUI 是系統(tǒng)應(yīng)用,所以它也是一個(gè) APK,有入口 Application,只不過(guò)它是由 system_server 進(jìn)程直接啟動(dòng)的。
關(guān)于SystemServer,它是 Android framework 中關(guān)鍵系統(tǒng)的服務(wù),由 Android 系統(tǒng)最核心的進(jìn)程Zygotefork 生成,進(jìn)程名為system_server。常見(jiàn)的ActivityManagerService、PackageManagerService、WindowManageService都是由SystemServer啟動(dòng)的。
SystemServer 源碼路徑:/frameworks/base/services/java/com/android/server/SystemServer.java
第一步:SystemServer 的 main() 方法中調(diào)用 SystemServer.run(),run()中調(diào)用startOtherServices()
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
... ...
// Start services.
try {
startBootstrapServices(t);
startCoreServices(t);
startOtherServices(t); //SystemServer在startOtherServices()被啟動(dòng)
}
... ...
}
第二步:startOtherServices()中通過(guò)AMS的回調(diào)方法ready(),然后調(diào)用startSystemUi()
mActivityManagerService.systemReady(() -> {
... ...
try {
startSystemUi(context, windowManagerF);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
... ...
,t);
第三步:startSystemUi()中可以看出,SystemUI本質(zhì)就是一個(gè)Service,通過(guò)PM獲取到的Component 是com.android.systemui/.SystemUIService,然后通過(guò)調(diào)用context.startServiceAsUser()完成對(duì)SystemUIService的啟動(dòng)。
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應(yīng)用,所以SystemUIService啟動(dòng)前需要完成SystemUI整個(gè)應(yīng)用的啟動(dòng),其流程也就是應(yīng)用常見(jiàn)的冷啟動(dòng)流程,這里展開(kāi)講一下:
SystemUI 應(yīng)用啟動(dòng)流程 context中的startServiceAsUser()是一個(gè)抽象方法,具體實(shí)現(xiàn)在ContextImpl.java里。實(shí)現(xiàn)方法startServiceCommon()中,通過(guò)ActivityManager.getService()就會(huì)走到AMS中,最終在AMS來(lái)啟動(dòng)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中開(kāi)啟Service
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
getOpPackageName(), getAttributionTag(), user.getIdentifier());
... ...
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
接下來(lái)進(jìn)入AMS,一探究竟:
AMS 源碼路徑:/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
在AMS的startService()方法里,會(huì)經(jīng)過(guò)一系列內(nèi)部流程,調(diào)用到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); // 內(nèi)部調(diào)用到 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的進(jìn)程信息
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
... ...
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting); //內(nèi)部調(diào)用到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); //調(diào)用到bringUpServiceLocked()
if (error != null) {
return new ComponentName("!!", error);
}
... ...
return r.name;
}
繼續(xù)調(diào)用了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進(jìn)程存在
if (app != null && app.thread != null) {
try {
app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
//啟動(dòng)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);
}
}
}
... ...
// 如果不存在此進(jìn)程
if (app == null && !permissionsReviewRequired) {
// 啟動(dòng)運(yùn)行的線程
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;
}
這個(gè)方法做了兩件事:
如果SystemUIService所屬進(jìn)程已經(jīng)存在,則直接調(diào)用realStartServiceLocked()。如果SystemUIService所屬進(jìn)程不存在,則執(zhí)行startProcessLocked()方法創(chuàng)建進(jìn)程,經(jīng)過(guò)層層調(diào)用,最終也會(huì)走到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;
}
}
這個(gè)方法內(nèi)部調(diào)用了app.thread.scheduleCreateService(),而app.thread是一個(gè)IApplicationThread類(lèi)型的,他的實(shí)現(xiàn)是ActivityThread的一個(gè)內(nèi)部類(lèi)ApplicationThread,而這個(gè)類(lèi)正好實(shí)現(xiàn)了IApplicationThread.Stub,在ApplicationThread類(lèi)中,找到對(duì)應(yīng)的調(diào)用方法:
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);
}
可以看出,是發(fā)送一個(gè)消息給Handler,這個(gè)Handler是ActivityThread的內(nèi)部類(lèi)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;
... ...
}
}
最終調(diào)用了handleCreateService()方法:
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
//創(chuàng)建service的context
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
//創(chuàng)建Application
Application app = packageInfo.makeApplication(false, mInstrumentation);
//獲取類(lèi)加載器
java.lang.ClassLoader cl = packageInfo.getClassLoader();
//加載service實(shí)例
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());
//調(diào)用service的onCreate方法
service.onCreate();
mServices.put(data.token, service);
try {
//通過(guò)serviceDoneExecuting告知AMS,service已經(jīng)啟動(dòng)完成
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);
}
}
}
這個(gè)方法主要做了以下幾件事:
首先,創(chuàng)建上下文創(chuàng)建SystemUIApplication,獲取類(lèi)加載器加載SystemUIService實(shí)例,初始化SystemUIService, 調(diào)用onCreate()方法最后通知AMS,SystemUIService啟動(dòng)完成。
到這里SystemUIService已經(jīng)啟動(dòng)完成。
第五步: 前面在SystemUIApplication創(chuàng)建成功后會(huì)回調(diào)內(nèi)部的OnCreate()方法,在OnCreate()中方法注冊(cè)了一個(gè)開(kāi)機(jī)廣播,當(dāng)接收到開(kāi)機(jī)廣播后會(huì)調(diào)用SystemUI的onBootCompleted()方法來(lái)告訴每個(gè)子模塊 Android 系統(tǒng)已經(jīng)完成開(kāi)機(jī)。
@Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "SystemUIApplication created.");
// 設(shè)置所有服務(wù)繼承的應(yīng)用程序主題。
// 請(qǐng)注意,在清單中設(shè)置應(yīng)用程序主題僅適用于activity。這里是讓Service保持與主題設(shè)置同步。
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 {
// 我們不需要為正在執(zhí)行某些任務(wù)的子進(jìn)程啟動(dòng)服務(wù)。
...
}
}
2.2 SystemUI 中的流程
第六步:SystemUIService初始化完成后會(huì)調(diào)用onCreate()方法,onCreate()中調(diào)用了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(); //調(diào)用startServicesIfNeeded()
... ...
}
}
第七步: 在SystemUIApplication的startServicesIfNeeded()方法中,通過(guò)SystemUIFactory獲取到配置在config.xml中每個(gè)子模塊的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
第八步: 在startServicesIfNeeded()中通過(guò)反射完成了每個(gè)SystemUI組件的創(chuàng)建,然后再調(diào)用各個(gè)SystemUI的onStart()方法來(lái)繼續(xù)執(zhí)行子模塊的初始化。
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]);
// 調(diào)用各個(gè)子模塊的start()
mServices[i].start();
// 首次啟動(dòng)時(shí),這里始終為false,不會(huì)被調(diào)用
if (mBootCompleteCache.isBootComplete()) {
mServices[i].onBootCompleted();
}
}
mServicesStarted = true;
}
這里的SystemUI是一個(gè)抽象類(lèi),狀態(tài)欄、近期任務(wù)等等模塊都是繼承自SystemUI,通過(guò)這種方式可以很大程度上簡(jiǎn)化復(fù)雜的SystemUI程序中各個(gè)子模塊創(chuàng)建方式,同時(shí)我們可以通過(guò)配置資源的方式動(dòng)態(tài)加載需要的SystemUI模塊。
SystemUI的源碼如下,方法基本都能見(jiàn)名知意,就不再介紹了。
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 的啟動(dòng)流程
前文提到CarSystemUI復(fù)用了手機(jī)SystemUI的代碼,所以CarSystemUI的啟動(dòng)流程和SystemUI的是完全一致的。
但CarSystemUI中需要的功能與SystemUI中也有部分差異,那么是這些差異化的功能是如何引入并完成初始化?以及一些手機(jī)的SystemUI才需要的功能是如何去除的呢?
其實(shí)很簡(jiǎn)單,在SystemUI的啟動(dòng)流程中我們得知,各個(gè)子模塊的 className 是通過(guò)SystemUIFactory的getSystemUIServiceComponents()獲取到的,那么只要繼承SystemUIFactory并重寫(xiě)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
// 先引入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;
}
}
通過(guò)以上方式,就完成了CarSystemUI子模塊的替換。
2.4 小結(jié)
總結(jié)一下,SystemUI的大致啟動(dòng)流程可以歸納如下:
SystemUI 是一個(gè) persistent 應(yīng)用,它由操作系統(tǒng)啟動(dòng),主要流程為
Android 系統(tǒng)在開(kāi)機(jī)后會(huì)創(chuàng)建 system_server 進(jìn)程,它會(huì)啟動(dòng)各種系統(tǒng)所需要的服務(wù),其中就包括 SystemUIService。SystemUIService 啟動(dòng)后進(jìn)入到應(yīng)用層 SystemUI 中,在 SystemUIApplication 它首先會(huì)初始化監(jiān)聽(tīng)ACTION_BOOT_COMPLETED 等通知,待系統(tǒng)完成啟動(dòng)后會(huì)通知各個(gè)組件 onBootCompleted。在進(jìn)入 SystemUIService 中依然執(zhí)行的 SystemUIApplication 中的startServicesIfNeeded() 方法啟動(dòng) SystemUI 中的子模塊。最終的服務(wù)啟動(dòng)邏輯都是在 SystemUIApplication 里面,并且都保存在 mServices 數(shù)組中。
三 總結(jié)
SystemUI在原生的車(chē)載 Android 系統(tǒng)是一個(gè)較為復(fù)雜的模塊,本文主要介紹了SystemUI 和CarSystemUI的功能、源碼結(jié)構(gòu)及啟動(dòng)時(shí)序,希望能幫到從事SystemUI開(kāi)發(fā)的同學(xué)。
四 參考文檔
Android 車(chē)載應(yīng)用開(kāi)發(fā)與分析(12) - SystemUI (一)
精彩鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。