0


Android 车载应用开发指南(3) - SystemUI 详解

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;}

这个方法做了两件事:

  1. 如果SystemUIService所属进程已经存在,则直接调用realStartServiceLocked()
  2. 如果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);}}}

这个方法主要做了以下几件事:

  1. 首先,创建上下文
  2. 创建SystemUIApplication,获取类加载器
  3. 加载SystemUIService实例,初始化SystemUIService, 调用onCreate()方法
  4. 最后通知AMSSystemUIService启动完成。

到这里

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 (一)

标签: android 车载系统 ui

本文转载自: https://blog.csdn.net/liaoshemeng8432/article/details/136071955
版权归原作者 话唠扇贝 所有, 如有侵权,请联系我们删除。

“Android 车载应用开发指南(3) - SystemUI 详解”的评论:

还没有评论