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()
publicstaticvoidmain(String[] args){newSystemServer().run();}
privatevoidrun(){......// 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
的启动。
privatestaticvoidstartSystemUi(Context context,WindowManagerService windowManager){PackageManagerInternal pm =LocalServices.getService(PackageManagerInternal.class);Intent intent =newIntent();
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
。
@OverridepublicComponentNamestartServiceAsUser(Intent service,UserHandle user){returnstartServiceCommon(service,false, user);}@OverrideprivateComponentNamestartServiceCommon(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()
方法。
@OverridepublicComponentNamestartService(IApplicationThread caller,Intent service,String resolvedType,boolean requireForeground,String callingPackage,String callingFeatureId,int userId)throwsTransactionTooLargeException{......try{
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, callingFeatureId, userId);// 内部调用到 startServiceLocked()}......}}ComponentNamestartServiceLocked(IApplicationThread caller,Intent service,String resolvedType,int callingPid,int callingUid,boolean fgRequired,String callingPackage,@NullableString callingFeatureId,finalint userId,boolean allowBackgroundActivityStarts)throwsTransactionTooLargeException{......if(caller !=null){// 这里记录app的进程信息finalProcessRecord callerApp = mAm.getRecordForAppLocked(caller);......ComponentName cmp =startServiceInnerLocked(smap, service, r, callerFg, addToStarting);//内部调用到startServiceInnerLocked()......return cmp;}ComponentNamestartServiceInnerLocked(ServiceMap smap,Intent service,ServiceRecord r,boolean callerFg,boolean addToStarting)throwsTransactionTooLargeException{......String error =bringUpServiceLocked(r, service.getFlags(), callerFg,false,false);//调用到bringUpServiceLocked()if(error !=null){returnnewComponentName("!!", error);}......return r.name;}
继续调用了
bringUpServiceLocked()
方法,
privateStringbringUpServiceLocked(ServiceRecord r,int intentFlags,boolean execInFg,boolean whileRestarting,boolean permissionsReviewRequired)throwsTransactionTooLargeException{......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);//启动servicerealStartServiceLocked(r, app, execInFg);returnnull;}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;}}......returnnull;}
这个方法做了两件事:
- 如果
SystemUIService
所属进程已经存在,则直接调用realStartServiceLocked()
。 - 如果
SystemUIService
所属进程不存在,则执行startProcessLocked()
方法创建进程,经过层层调用,最终也会走到realStartServiceLocked()
中:
privatefinalvoidrealStartServiceLocked(ServiceRecord r,ProcessRecord app,boolean execInFg)throwsRemoteException{......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
类中,找到对应的调用方法:
publicfinalvoidscheduleCreateService(IBinder token,ServiceInfo info,CompatibilityInfo compatInfo,int processState){updateProcessState(processState,false);CreateServiceData s =newCreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;sendMessage(H.CREATE_SERVICE, s);}
可以看出,是发送一个消息给
Handler
,这个
Handler
是
ActivityThread
的内部类
H
publicvoidhandleMessage(Message msg){switch(msg.what){......caseCREATE_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()
方法:
privatevoidhandleCreateService(CreateServiceData data){LoadedApk packageInfo =getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);Service service =null;try{//创建service的contextContextImpl context =ContextImpl.createAppContext(this, packageInfo);//创建ApplicationApplication 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(newResourcesLoader[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)){thrownewRuntimeException("Unable to create service "+ data.info.name
+": "+ e.toString(), e);}}}
这个方法主要做了以下几件事:
- 首先,创建上下文
- 创建
SystemUIApplication
,获取类加载器 - 加载
SystemUIService
实例,初始化SystemUIService
, 调用onCreate()
方法 - 最后通知
AMS
,SystemUIService
启动完成。
到这里
SystemUIService
已经启动完成。
第五步: 前面在
SystemUIApplication
创建成功后会回调内部的
OnCreate()
方法,在
OnCreate()
中方法注册了一个开机广播,当接收到开机广播后会调用
SystemUI
的
onBootCompleted()
方法来告诉每个子模块 Android 系统已经完成开机。
@OverridepublicvoidonCreate(){super.onCreate();Log.v(TAG,"SystemUIApplication created.");// 设置所有服务继承的应用程序主题。// 请注意,在清单中设置应用程序主题仅适用于activity。这里是让Service保持与主题设置同步。setTheme(R.style.Theme_SystemUI);if(Process.myUserHandle().equals(UserHandle.SYSTEM)){IntentFilter bootCompletedFilter =newIntentFilter(Intent.ACTION_BOOT_COMPLETED);
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);registerReceiver(newBroadcastReceiver(){@OverridepublicvoidonReceive(Context context,Intent intent){if(mBootCompleteCache.isBootComplete())return;if(DEBUG)Log.v(TAG,"BOOT_COMPLETED received");unregisterReceiver(this);
mBootCompleteCache.setBootComplete();if(mServicesStarted){finalintN= 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
publicclassSystemUIServiceextendsService{......@OverridepublicvoidonCreate(){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
// SystemUIApplicationpublicvoidstartServicesIfNeeded(){String[] names =SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());startServicesIfNeeded("StartServices", names);}// SystemUIFactory/** Returns the list of system UI components that should be started. */publicString[]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-arrayname="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()
方法来继续执行子模块的初始化。
privateSystemUI[] mServices;privatevoidstartServicesIfNeeded(String metricsPrefix,String[] services){if(mServicesStarted){return;}
mServices =newSystemUI[services.length];...finalintN= 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){thrownewRuntimeException(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
的源码如下,方法基本都能见名知意,就不再介绍了。
publicabstractclassSystemUIimplementsDumpable{protectedfinalContext mContext;publicSystemUI(Context context){
mContext = context;}publicabstractvoidstart();protectedvoidonConfigurationChanged(Configuration newConfig){}@Overridepublicvoiddump(@NonNullFileDescriptor fd,@NonNullPrintWriter pw,@NonNullString[] args){}protectedvoidonBootCompleted(){}
2.3 CarSystemUI 的启动流程
前文提到
CarSystemUI
复用了手机
SystemUI
的代码,所以
CarSystemUI
的启动流程和
SystemUI
的是完全一致的。
但
CarSystemUI
中需要的功能与
SystemUI
中也有部分差异,那么是这些差异化的功能是如何引入并完成初始化?以及一些手机的
SystemUI
才需要的功能是如何去除的呢?
其实很简单,在
SystemUI
的启动流程中我们得知,各个子模块的 className 是通过
SystemUIFactory
的
getSystemUIServiceComponents()
获取到的,那么只要继承
SystemUIFactory
并重写
getSystemUIServiceComponents()
就可以了。
publicclassCarSystemUIFactoryextendsSystemUIFactory{@OverrideprotectedSystemUIRootComponentbuildSystemUIRootComponent(Context context){returnDaggerCarSystemUIRootComponent.builder().contextHolder(newContextHolder(context)).build();}@OverridepublicString[]getSystemUIServiceComponents(Resources resources){Set<String> names =newHashSet<>();// 先引入systemUI中的componentsfor(String s :super.getSystemUIServiceComponents(resources)){
names.add(s);}// 再移除CarsystemUI不需要的componentsfor(String s : resources.getStringArray(R.array.config_systemUIServiceComponentsExclude)){
names.remove(s);}// 最后再添加CarsystemUI特有的componentsfor(String s : resources.getStringArray(R.array.config_systemUIServiceComponentsInclude)){
names.add(s);}String[] finalNames =newString[names.size()];
names.toArray(finalNames);return finalNames;}}
<!-- 需要移除的Components. --><string-arrayname="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-arrayname="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
开发的同学。
四 参考文档
Android 车载应用开发与分析(12) - SystemUI (一)
版权归原作者 话唠扇贝 所有, 如有侵权,请联系我们删除。