wordpress的搭建环境搭建,seo网站推广的目的包括哪个方面,wordpress关注公众号发送验证码,网站开发 有哪些优化功能Android 12 源码分析 —— 应用层 二#xff08;SystemUI大体组织和启动过程#xff09;
在前一篇文章中#xff0c;我们介绍了SystemUI怎么使用IDE进行编辑和调试。这是分析SystemUI的最基础#xff0c;希望读者能尽量掌握。
本篇文章#xff0c;将会介绍SystemUI的大概…Android 12 源码分析 —— 应用层 二SystemUI大体组织和启动过程
在前一篇文章中我们介绍了SystemUI怎么使用IDE进行编辑和调试。这是分析SystemUI的最基础希望读者能尽量掌握。
本篇文章将会介绍SystemUI的大概组织架构以及它的启动过程。本篇文章读完将会知道
SystemUI为什么选择使用Dagger2SystemUI怎么新建一个模块SystemUI的启动流程
在进行阅读之前请跟着我思考如下的问题
SystemUI要完成哪些功能各个功能之间需要沟通吗倘若各个功能之间需要进行沟通怎样组织他们之间的引用关系各个功能需要与系统服务沟通吗倘若各个功能需要与系统服务沟通怎么高效的组织他们之间的引用各个功能之间有依赖关系吗各个功能之间启动有顺序要求吗
针对上述123问题我们可以从界面上看到SystemUI包含锁屏状态栏导航栏Toast的显示音量调节等等功能。这些功能之间可能会相互引用如状态栏模块需要知道锁屏模块的情况便于决定是否要隐藏一些状态栏图标。
这些功能多多少少会与系统之间进行交互如锁屏模块需要知道电源按钮的情况便于决定是否要显示或者隐藏锁屏再如各个功能模块是否要显示夜间模式等等。
从这里我们已经知道各个模块之间需要相互引用且与系统之间也会有沟通为此需要设计好架构让各个模块便于获得想要的对象而这些对象的创建可能又依赖于其他对象的创建。事实上SystemUI的各个对象之间依赖关系比较复杂如果手动创建各个对象需要写非常多的代码。为此我们使用新的组件管理方式DI(依赖注入)
依赖注入的核心思想就是在编写代码的时候不再由开发人员显示的去编辑各个对象应该怎么创建而是由依赖注入库去决定怎么创建对象。SystemUI选择了Dagger2作为其依赖注入库。 注意这里需要说明什么叫做依赖依赖就是一个组件A在完成功能的过程中需要用到另外一个组件B则叫做A依赖B。AB可以是类对象模块等等。那么什么叫做注入呢注入就是将依赖项提供给组件的过程。 现在设计好了各个模块之间怎么得到想要的对象——Dagger依赖注入。
下面想一想SystemUI和系统之间该怎么去沟通呢这就涉及到应该怎么划分SystemUISystemUI作为整个系统的基础UI组成在整个Android 系统中占据非常重大的地位。为了能够适应更加多元的场景如Android TVAndroid Car他们可能又不一样的SystemUI同时又为了满足模块化设计。故将SystemUI设计成一个常驻内存中的apk。
既然已经将SystemUI设计成了一个apk那么它就单独运行在另外一个进程中要与系统进行沟通那么只有通过Android的四大组件进行交互以及使用Android的Binder进行交互。那么SystemUI就变成了各种ServiceActivityBroadcastReceiverContentProvider的一个集合了。然后在合适的时候启用这些组件。
结合前面的思考各个功能模块如锁屏状态栏导航栏只要放入合适的组件之中就可以在正确的地方显示了。同时为了方便访问其他各个模块我们还使用了Dagger进行辅助。
看上去似乎一切美好但是真的美好吗是否漏掉了一个重要问题这些模块之间有先后顺序吗为了能快速的进入系统目前SystemUI不做这方面的要求如果某一个模块需要等待另外一个模块准备好之后才能正常工作那么就需要调整设计逻辑了。
查看SystemUI的组件有哪些
从上面我们知道SystemUI整体上被放入了四大组件之中那么我们查看AndroidManifest.xml看看都有哪些组件被定义了。
在android-12.0.0_r34分支上AndroidManifest.xml有38个Activity11个Service4个provider11个receiver
接下来我将给出各个组件的简单描述再后续的章节中我们将会细细介绍这些组件如何启动完成哪些功能。
Activity篇
LongScreenShotActivity:长截图使用的视图,用户决定长截图时调起这个Activity。ScreenRecordDialog:录屏时弹出的选项框视图。TunerActivity:这是给研发人员用的微调界面。可以使用下面命令打开界面入口
adb shell pm enable com.android.systemui/com.android.systemui.tuner.TunerActivity然后在设置-系统-System UI Tuner进入界面
DemoModeSystemUI的Demo模式也是给研发人员用的他是TunerActivity的功能补充可在开发者选项中打开ForceReSizableInfoActivity:弹出应用无法在分屏模式或者辅助屏幕下运行UsbPermissionActivity:确定USB权限弹框UsbResolverActivity:为USB设备选择一个应用弹框UsbConfirmActivity:弹出一个视图用来确定是否要使用某个app,是UsbResolverActivity的后续视图SensorUseStartedActivity:当传感器在privacy mode下欲使用传感器时的弹框TvUnblockSensorActivity:同SensorUseStartedActivity只不过这个是运用在电视机上的视图UsbAccessoryUriActivity:弹出一个框让你去下载这个USB设备对应的应用UsbContaminantActivity:弹出一个框表示USB已经停用停用的原因可能是usb端口处有赃物等。UsbDebuggingActivity:弹出是否允许USB调试UsbDebuggingActivityAlias:这个是UsbDebuggingActivity的别名WifiDebuggingActivity:弹出是否允许网络进行无线调试WifiDebuggingActivityAlias:是WifiDebuggingActivity的别名WifiDebuggingSecondaryUserActivity:弹出目前登录的用户无法开启无线调试功能需要切换为主用户NetworkOverLimitActivity:弹出数据流量已经达到上限MediaProjectionPermissionActivity:多媒体投屏权限确认TvNotificationPanelActivity:TV专用弹出一个消息框SlicePermissionActivity:Slice权限弹框DessertCase:彩蛋之一MLandActivity:彩蛋小游戏PeopleSpaceActivity:提示Pepole Space UI的位置,android 11新增功能LaunchConversationActivity:当会话被点击的时候展开视图Android 11 新增功能WorkLockActivity:解锁work profile的界面CreateUserActivity:创建用户视图Somnambulator:屏保BrightnessDialog亮度弹框ForegroundServicesDialog:展示前台services的一个弹框ChooserActivity:弹出一个框让用户选择打开哪一个应用来处理当前的IntentControlsProviderSelectorActivity 弹出“选择要添加控制器的应用”ControlsEditingActivity:编辑控制器拖拽进行编辑ControlsFavoritingActivity:控制器偏好设置ControlsActivity:列出设备控制器WalletActivity:电子钱包ControlsRequestDialog:control请求添加设备控制器弹框 注意这里的Controls是外部设备的控制器如全屋智能中的控制器。 上面只是一个非常简单的概览而一些常见组件逻辑和UI细节将会在后续文章中出现。
看到这里可能会有读者提问上面的Activity似乎没有状态栏和锁屏呀他们的视图难道不在这些Activity里面吗
欲探讨这个问题还需要先看剩下的组件。
Services篇
SystemUIService哇哦多么让人提神的名字这个Service包含了SystemUI内部的大部分功能它也是我们SystemUI源码分析的重中之重。SystemUISecondaryUserService:多用户情况下该service保证多用户的SystemUI功能正常SystemUIAuxiliaryDumpService:开发使用dump出各个必要部件的信息并查看TakeScreenshotService:截屏相关的serviceRecordingService:录屏相关的serviceImageWallpaper:壁纸相关的servicePeopleBackupFollowUpJob:People service ui相关的服务DessertCaseDream:小彩蛋KeyguardService:锁屏相关的服务AuxiliaryPersistenceWrapper$DeletionJobService:外部设备控制器相关的服务DozeService:跟Doze相关的服务
ContentProvider篇
FileProvider:提供文件KeyguardSliceProvider:提供锁屏SliceClockOptionsProvider:为选择器程序提供时钟预览PeopleProvider:返回给定快捷方式的 People Tile 预览
BroadcastReceiver篇
ScreenshotServiceErrorReceiver:截屏失败广播接收器SysuiRestartReceiver:重启SystemUI广播接收器ActionProxyReceiver:拦截share和edit intent便于提前处理一些事情的广播接收器DeleteScreenshotReceiver:删除截屏广播接收器SmartActionsReceiver:用户点击通知中的smart action之后用于接收对应的广播并执行smart actionControlsRequestReciver:接收增加控制器请求的广播接收器TunerService$ClearReciver:用于调用TunerService的clear的广播接收器KeyboardShortcutsReceiver:展示或者隐藏键盘快捷键的广播接收器MediaOutputDialogReceiver:接收媒体输出Intent的广播接收器PeopleSpaceWidgetPinnedReceiver:当一个联系人Tile widget被添加之后这个接收器被调用PeopleSpaceWidgetProvider:People Space widget 的实现
阅读到这个地方读者依然会有疑问——SystemUI的锁屏和状态栏到底在什么地方显示出来的Service里面能显示UI吗明显不合理呀。那么安卓的锁屏和状态栏到底是怎么显示出来的呢 小提示SystemUI除了上面列出的组件来显示视图以外还通过直接与WindowManager交互来显示视图。What~~ 是不是会感叹一句android的设计架构还真是有些混乱。 此处不表后续详解。接下来我们需要先处理前面提到的关于Dagger2它是如何处理SystemUI中各个组件的引用关系的。
SystemUI内部组件设计
我们要让Dagger2来管理各个组件的依赖关系那我我们必然要告诉Dagger2有怎样的依赖关系应该使用什么样的方式呢用xml文件来描述吗还是用其他的方式呢
Dagger2使用了java的注解来描述他们之间的依赖关系。同时为了提升性能Dagger2会在编译的时候根据注解生成不同的java对象然后在生成的java对象中安排好了一切的依赖以及生命周期。
用来表示各种依赖关系的注解叫做给Dagger2画一副图(graph).接下来的我们结合SystemUI中的实例看看SystemUI如何给Dagger2画了一副图。
Dagger2 在SystemUI中的应用
在我们的设想中需要一个最最顶部的对象比如RootManager.然后根据这个RootManager来获得我们需要的各个对象。
在SystemUI中依然也有这么个RootManager。它就是GlobalRootComponent。SystemUI的各个模块想要拿到自己想要的对象可以通过GlobalRootComponent获取。 注意读者看到这里肯定会非常疑惑为什么要叫Component而不是Manager毕竟Manager在Android中多么的常见。这是因为SystemUI使用了Dagger2的抽象。在Dagger2中Component表示一个组件事实上它是一个容器里面包含有它可以提供的所有依赖。故此GlobalRootComponent则是可以提供给所有依赖的一个组件。 那么我们来看看如何给GlobalRootComponent画图的。
//Singeton:告诉Dagger2所有带有singtone注解的对象生命周期一致。此处表示全局唯一
//Component(xxx):告诉Dagger2定义了一个Component组件
//modules{xxx}:告诉Dagger2,这个组件依赖这些模块。关于模块的概念见后文
Singleton
Component(modules {GlobalModule.class,SysUISubcomponentModule.class,WMModule.class})
//此处是interface接口定义Dagger2会生成对应的实现类并按照我们给Dagger2的注解图管理好
//各个对象的依赖和创建
public interface GlobalRootComponent {//Component.Builder://告诉Dagger2这个是创建GlobalRootComponent的Builder类//请务必要思考为什么此处的对象创建要用Builder模式而不是工厂模式Component.Builderinterface Builder {BindsInstanceBuilder context(Context context);GlobalRootComponent build();}//提供一个方法这个方法的返回类型就是这个组件可以提供的依赖此处表示可以提供//WMComponent.Builder对象WMComponent.Builder getWMComponentBuilder();//表示此组件可以提供SysUIComponent.Builder对象SysUIComponent.Builder getSysUIComponent();//注上面两个方法提供的返回类型可以看到他们依然是一个Component//表示可以提供ThreadFactory对象ThreadFactory createThreadFactory();
}在上面的例子中我们提到了模块module这个概念在介绍这个概念之前先思考一个问题如果GlobalRootComponent中有很多很多依赖怎么办呢如果每个都画在图上就会显得杂乱无章因此dagger2提供一种功能将这些不同的依赖用Module进行逻辑上面的划分。然后只需要在给Component画图时进行如下指定即可
Component(modules{xxx.class})我们选取SysUISubComponentModule进行查看源码如下
//Module:告诉Dagger2这个module内的所有依赖逻辑划分为SysUISubcomponentModule
//subcomponents {SysUIComponent.class}:告诉Dagger2,这个模块含有SysUIComponent子组件
//关于子组件的概念我们下文介绍
Module(subcomponents {SysUIComponent.class})
public abstract class SysUISubcomponentModule {
}在上面的例子中我们提到了subcomponent,在介绍这个概念之前我们再来想个问题如果一个Component提供一个对象给其他使用者被提供的对象应该是每次都创建还是只创建一次呢这就涉及到对象的生命周期为了能够更好的管理生命周期我们建议同属于一个生命周期的对象放在一个子组件中。因此上面的SysUIComponent子组件中所有对象将属于同一个生命周期。当然subcomponent也不仅仅可以隔离生命周期还可以隔离模块使代码更加清晰
那么我们看看这个subcomponent是怎么被告知Dagger2的。
//SysUISingleton告诉Dagger2所有SysUISingleton注解的对象生命周期相同
//Subcomponent:告诉Dagger2这是一个子组件
//modules{xxx}:告诉dagger2这个子组件有这么些module的需要依赖
SysUISingleton
Subcomponent(modules {DefaultComponentBinder.class,DependencyProvider.class,SystemUIBinder.class,SystemUIModule.class,SystemUIDefaultModule.class})
public interface SysUIComponent {//告诉Dagger2生命周期SysUISingleton//告诉Dagger2这个子组件的Builder接口定义Subcomponent.Builderinterface Builder {//省略若干相同部分//BindsInstance告诉Dagger2将t绑定到这个Builder对象中//在Dagger2根据我们画的图会根据这个Builder接口生成一个SysUIComponentBuilder对象//在这个对象中会有一个成员类型为OptionalTaskSurfaceHelper名字为setTaskSurfaceHelper.//然后这个setTaskSurfaceHelper()接口函数的实现就会将参数传入的t保存在setTaskSurfaceHelper成员中。//这个过程就叫做绑定实例也即BindsInstance的语义BindsInstanceBuilder setTaskSurfaceHelper(OptionalTaskSurfaceHelper t);//任何一个Builder接口都必须有一个build()函数且返回类型为需要构建的对象类型此处即为SysUIComponentSysUIComponent build();}//定义了一个默认方法这个方法什么也没有做default void init() {// Do nothing}//告诉Dagger2它的生命周期SysUISingleton//subcomponent和component一样如果想要对外提供依赖就可以定义任何一个函数函数的返回类型就是//被提供对象的类型。BootCompleteCacheImpl provideBootCacheImpl();//省略若干相同部分//当返回类型为空而传入类型不为空的时候表示需要向传入类型对象SystemUIAppComponentFactory//中被inject标记的成员赋值叫做注入//理论上函数名为任意值但是此种函数几乎只会完成注入的功能因此此函数最后都叫做injectvoid inject(SystemUIAppComponentFactory factory);//省略若干相同部分
}在上面的inject函数中我们可以看看SystemUIAppComponentFactory是怎么样的源码如下
public class SystemUIAppComponentFactory extends AppComponentFactory {//Inject告诉Dagger2这个成员需要Dagger2的注入。//可是Dagger2又是如何知道该怎么创建ContextComponentHelper的呢//这就是我们给Dagger2画图的作用我们已经提前画好图给Dagger2告诉它应该//怎么创建这个ContextComponentHelperInjectpublic ContextComponentHelper mComponentHelper;}
接下来看看我们是怎么给ContextComponentHelper画图的源码如下
public interface ContextComponentHelper {//省略若干无用部分
}
从上面的源码可以知道它没有任何注解即它没有被画如Dagger2的图中被画入Dagger2图的另有它类ContextComponentHelper的实现类为ContextComponentResolver,源码如下
//SysUISingleton:告诉Dagger2它的生命周期
SysUISingleton
public class ContextComponentResolver implements ContextComponentHelper {private final MapClass?, ProviderActivity mActivityCreators;private final MapClass?, ProviderService mServiceCreators;private final MapClass?, ProviderSystemUI mSystemUICreators;private final MapClass?, ProviderRecentsImplementation mRecentsCreators;private final MapClass?, ProviderBroadcastReceiver mBroadcastReceiverCreators;//Inject:此处就是告诉Dagger图注入Dagger2的各种辅助功能帮助创建这个对象//在创建对象的时候需要它的各种参数而这些参数又应该怎么被Dagger2提供呢//只要我们把需要的参数画好图给Dagger2即可过程就和这个ContextComponentResolver一样啦//在构造器上面标注一下Inject就可以了InjectContextComponentResolver(MapClass?, ProviderActivity activityCreators,MapClass?, ProviderService serviceCreators,MapClass?, ProviderSystemUI systemUICreators,MapClass?, ProviderRecentsImplementation recentsCreators,MapClass?, ProviderBroadcastReceiver broadcastReceiverCreators) {mActivityCreators activityCreators;mServiceCreators serviceCreators;mSystemUICreators systemUICreators;mRecentsCreators recentsCreators;mBroadcastReceiverCreators broadcastReceiverCreators;}//省略若干无用部分
}看到此处我们已经大体知道了Dagger2该怎么使用该怎么提供一个图给它。但是仔细思考依然会有一个问题——要是某些类不是由我们创建那么我们就没法在构造器上面加上Inject了那么怎么才能让Dagger2知道:当它需要这个对象的时候应该怎么创建呢此时Dagger2提供了另外一个注解Provides .
我们以GlobalModule为例进行说明源码如下
//Module告诉Dagger2定义一个逻辑模块,这个模块包含FrameworkServicesModule,GlobalConcurrencyModule
Module(includes {FrameworkServicesModule.class,GlobalConcurrencyModule.class})
public class GlobalModule {//Provides告诉Dagger2如果需要DisplayMetrics对象就调用provideDisplayMetrics()函数即可//至于这个函数需要的参数Context该怎么创建Dagger2已经能够从我们给它的图中自动找到了Providespublic DisplayMetrics provideDisplayMetrics(Context context) {DisplayMetrics displayMetrics new DisplayMetrics();context.getDisplay().getMetrics(displayMetrics);return displayMetrics;}}至此我们已经大体介绍完了Dagger2中如何画图即怎么使用注解如怎么使用ComponentSubComponent,Inject,Provides,Module等等Dagger2中还有其他注解没有引入但是这已经足够本文接下来的阅读了。关于其他注解的内容可直接参考Dagger2的文档本文只专注于SystemUI的分析
可是上面只是画图并没有提到怎么使用的接下来我们将结合SystemUI的启动过程看看怎么使用上面画出来的Dagger2的图。
SystemUI的启动过程
任何一个Apk的启动都是从它的四大组件开始启动而在四大组件开始启动之前会去查看是否有自定义的Application如果有则会先创建Application。
从Android 9开始增加了一个AppComponentFactory用来在创建四大组件之前进行相应的操作。它同Application一样被配置在了AndroidManifest.xml中如下
applicationandroid:name.SystemUIApplication...android:appComponentFactory.SystemUIAppComponentFactory!--省略若干不相干话题--
/application从这个配置中我们可以看到如下的启动过程
SystemUIAppComponentFactory-SystemUIApplication-某个欲启动的组件(Android四大组件)。
在进一步分析这个流程之前先来看看谁启动了SystemUI
system_server的出发点
可是有读者会问SystemUI到底是由谁启动的呢你看其他的app都是通过点击图标启动SystemUI是由谁启动的
正确答案SystemUI由Android系统中一个叫做sytstem_server的进程启动system_server在开机的时候启动然后由system_server启动各种关键服务其中就包括启动SystemUI.这在后面分析system_server时会详细讲解.
此处只给出system_server启动SystemUI的简略说明
系统启动system_server进程之后会执行
new SystemServer().run();在run()方法中会去启动各种服务这些服务包括
启动引导服务核心服务其他服务 在启动其他服务的时候会去启动SystemUI(通过Intent)。而要获得启动SystemUI的具体组件就通过 Android的PackageManager得到 而PacakgeManager则通过读取配置config_systemUIServiceComponent得到具体的组件名。Android系统中这个配置 为
string nameconfig_systemUIServiceComponent translatablefalsecom.android.systemui/com.android.systemui.SystemUIService/string可见这正是我们在SystemUI中定义的组件。
那么我们就可以总结一下SystemUI的启动过程了
Android系统启动完成启动system_serversystem_server,根据配置通过Intent来启动SystemUI的组件SystemUI在启动组件之前会先创建SystemUIAppComponentFactory对象,然后调用其相应方法接着SystemUI会创建SystemUIApplication然后调用其相应方法最后SystemUI会创建SystemUIService并调用相应方法在创建SystemUIService之前则又会调用在第3步中创建的SystemUIAppComponentFactory对象的相应方法
为何要使用SystemUIAppComponentFactory
SystemUIAppComponentFactory源码如下:
public class SystemUIAppComponentFactory extends AppComponentFactory {private static final String TAG AppComponentFactory;Injectpublic ContextComponentHelper mComponentHelper;public SystemUIAppComponentFactory() {super();}NonNullOverride//在创建Application之前这个函数被调用public Application instantiateApplicationCompat(NonNull ClassLoader cl, NonNull String className)throws InstantiationException, IllegalAccessException, ClassNotFoundException {//调用父类方法创建Application此处会创建AndroidManifest.xml中配置的类//也即SystemUIApplicationApplication app super.instantiateApplicationCompat(cl, className);//倘若创建的组件是ContextInitializer,则注册一个回调//请一定注意虽然此处创建了Application但是它还不能当做Context来使用if (app instanceof ContextInitializer) {((ContextInitializer) app).setContextAvailableCallback(context - {//1.在回调中首先创建SystemUIFactory对象SystemUIFactory.createFromConfig(context);//2.通过这个SystemUIFactory得到SysUIComponent//3.注入SystemUIAppComponentFactory中的成员见上一小节SystemUIFactory.getInstance().getSysUIComponent().inject(SystemUIAppComponentFactory.this);});}return app;}NonNullOverride//ContentProvider被创建之前该函数被回调public ContentProvider instantiateProviderCompat(NonNull ClassLoader cl, NonNull String className)throws InstantiationException, IllegalAccessException, ClassNotFoundException {//省略若干//此处没有列出内容原因是它的逻辑和上一个函数一样}NonNullOverridepublic Activity instantiateActivityCompat(NonNull ClassLoader cl, NonNull String className,Nullable Intent intent)throws InstantiationException, IllegalAccessException, ClassNotFoundException {//省略若干//此处没有列出内容原因是它的逻辑和上一个函数一样}NonNullOverridepublic Service instantiateServiceCompat(NonNull ClassLoader cl, NonNull String className, Intent intent)throws InstantiationException, IllegalAccessException, ClassNotFoundException {//判断是否为空如果是则再次注入//第一次注入在instantiateApplicationCompat()函数设置的回调中//这个回调由SystemUIApplication的onCreate()触发if (mComponentHelper null) {// This shouldnt happen, but does when a device is freshly formatted.// Bug filed against framework to take a look: http://b/141008541SystemUIFactory.getInstance().getSysUIComponent().inject(SystemUIAppComponentFactory.this);}//注意这里的Service的创建//1. 先查询mComponentHelper中是否有对应的Service//2. 如果有则直接用如果没有则调用父类方法创建//对于SystemUIService而言它的构造函数有Inject注解因此当调用mComponentHelper.resolveService时能够正确返回SystemUIService//请思考为什么这个不要系统自己创建//答案因为SystemUIService需要有其他依赖对象若是由系统创建那么必然会有//像SystemUIService.setXXX()之类的函数会增加代码和逻辑。如果由Dagger2来创建则不会有//这些烦恼Service service mComponentHelper.resolveService(className);if (service ! null) {return service;}return super.instantiateServiceCompat(cl, className, intent);}NonNullOverridepublic BroadcastReceiver instantiateReceiverCompat(NonNull ClassLoader cl,NonNull String className, Nullable Intent intent)throws InstantiationException, IllegalAccessException, ClassNotFoundException {//省略若干//此处没有列出内容原因是它的逻辑和上一个函数一样}}在这个类中最最主要的功能就三点
相应组件的创建如SystemUIApplicationSystemUIService等mComponentHelper的初始化也即通过注入实现而在Service组件创建之前可能会先查询mComponentHelper中是否已经有组件若有则直接使用
在查看SystemUIApplication 和SystemUIService的创建之前我们还是要再次思考一个问题 为什么要用SystemUIAppComponentFactory这个类这个类真的合理吗有更好的替代方案吗
我想答案就在SystemUIService的创建上。正是SystemUIAppComponentFactory这个类的使用才让我们更好的注入依赖
在进入SystemUIService之前最先创建的是SystemUIApplication。我们来看看它所做的工作。
SystemUIApplication
源码如下
public class SystemUIApplication extends Application implementsSystemUIAppComponentFactory.ContextInitializer {public SystemUIApplication() {super();Log.v(TAG, SystemUIApplication constructed.);// SysUI may be building without protolog preprocessing in some casesProtoLog.REQUIRE_PROTOLOGTOOL false;}Overridepublic void onCreate() {super.onCreate();Log.v(TAG, SystemUIApplication created.);//用于跟踪启动和关闭的时序数据TimingsTraceLog log new TimingsTraceLog(SystemUIBootTiming,Trace.TRACE_TAG_APP);log.traceBegin(DependencyInjection);//这就是初始化各种Dagger2依赖的地方这个回调在SystemUIAppComponentFactory中被设置mContextAvailableCallback.onContextAvailable(this);//有了Dagger2就是直接使用对应的组件mRootComponent SystemUIFactory.getInstance().getRootComponent();mSysUIComponent SystemUIFactory.getInstance().getSysUIComponent();mComponentHelper mSysUIComponent.getContextComponentHelper();mBootCompleteCache mSysUIComponent.provideBootCacheImpl();log.traceEnd();//设置主题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);//设置线程渲染优先级int sfPriority SurfaceControl.getGPUContextPriority();Log.i(TAG, Found SurfaceFlingers GPU Priority: sfPriority);if (sfPriority ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_REALTIME_NV) {Log.i(TAG, Setting SysUIs GPU Context priority to: ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);}//注册广播接收器registerReceiver(new BroadcastReceiver() {Overridepublic void onReceive(Context context, Intent intent) {//mBootCompleteCache表示的是:是否SytemUI的各种服务启动完成//这些服务的启动可能早于系统启动完成广播也可能晚于系统启动完成广播//1. 如果SystemUI的各种服务已经启动完成则直接返回if (mBootCompleteCache.isBootComplete()) return;if (DEBUG) Log.v(TAG, BOOT_COMPLETED received);//2. 如果没有启动完成则挨个启动unregisterReceiver(this);mBootCompleteCache.setBootComplete();if (mServicesStarted) {final int N mServices.length;for (int i 0; i N; i) {mServices[i].onBootCompleted();}}}}, bootCompletedFilter);//监听是否Local改变//如果Local改变则通知中的显示就需要改变如中英文切换等IntentFilter localeChangedFilter new IntentFilter(Intent.ACTION_LOCALE_CHANGED);registerReceiver(new BroadcastReceiver() {Overridepublic void onReceive(Context context, Intent intent) {if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {if (!mBootCompleteCache.isBootComplete()) return;// Update names of SystemUi notification channelsNotificationChannels.createAll(context);}}}, localeChangedFilter);} else {//如果是子进程则会进入此部分逻辑//如果是主用户下的子进程则什么也不做直接返回String processName ActivityThread.currentProcessName();ApplicationInfo info getApplicationInfo();if (processName ! null processName.startsWith(info.processName :)) {return;}//如果不是主用户则需要去启动必要的SystemUI组件startSecondaryUserServicesIfNeeded();}}//省略若干简单代码}SystemUIApplication的代码相对来讲比较简单都已经标记在注释里面了。接下来看看SystemUIService
SystemUIService
源码如下
public class SystemUIService extends Service {//省略若干简单代码//Inject嘿嘿这就是给Dagger画的图好让Dagger2知道怎么创建SystemUIServiceInjectpublic SystemUIService(Main Handler mainHandler,DumpHandler dumpHandler,BroadcastDispatcher broadcastDispatcher,LogBufferFreezer logBufferFreezer,BatteryStateNotifier batteryStateNotifier) {//省略赋值代码}Overridepublic void onCreate() {super.onCreate();//对没错startServicesIfNeeded作为整个SystemUI关键服务的启动源头就在这里了。//在进入分析之前先思考为什么要放在这里执行呢就不能直接放在SystemUIApplication中吗((SystemUIApplication) getApplication()).startServicesIfNeeded();//LogBufferFreezer接收bugreport开始的广播然后停止对应的LogBuffer的记录mLogBufferFreezer.attach(mBroadcastDispatcher);//是否监听电池的状态并且会提示在通知栏上if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) {mBatteryStateNotifier.startListening();}//调试代码用debug.crash_sysui触发RescuePartyif (Build.IS_DEBUGGABLE SystemProperties.getBoolean(debug.crash_sysui, false)) {throw new RuntimeException();}//Binder调试相关//如果太多binder调用就触发onLimitReached回调if (Build.IS_DEBUGGABLE) {//设置Binder代理计数开BinderInternal.nSetBinderProxyCountEnabled(true);//配置Binder代理触发BinderProxyLimitListener回调的最高和最低阈值最低表示:只有降到最低以下才能再次触发BinderInternal.nSetBinderProxyCountWatermarks(1000,900);//设置BinderProxyLimitListener监听BinderInternal.setBinderProxyCountCallback(new BinderInternal.BinderProxyLimitListener() {Overridepublic void onLimitReached(int uid) {Slog.w(SystemUIApplication.TAG,uid uid sent too many Binder proxies to uid Process.myUid());}}, mMainHandler);}//启动DumpService如果系统运行bugreportSystemUIAuxiliaryDumpService会将SystemUI中的一些关键数据dump出来startServiceAsUser(new Intent(getApplicationContext(), SystemUIAuxiliaryDumpService.class),UserHandle.SYSTEM);}//省略若干简单代码
}
接下来我们看看startServicesIfNeeded()函数的具体内容
startServicesIfNeeded()函数
该函数位于SystemUIApplication类内源码如下
public void startServicesIfNeeded() {//1. 获取需要start的服务列表//2. 然后调用startServicesIfNeed()继续启动//注意看到这里其实大家应该大胆假设getSystemUIServiceComponents函数//是不是通过Dagger2的依赖得到的。如果不是为什么String[] names SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());startServicesIfNeeded(/* metricsPrefix */ StartServices, names);
}private void startServicesIfNeeded(String metricsPrefix, String[] services) {//省略判断mServices new SystemUI[services.length];//启动完成缓存对象的修改简单略//首先获取DumpManagerfinal DumpManager dumpManager mSysUIComponent.createDumpManager();//trace跟踪点略//挨个启动服务// 1. 首先查看mComponentHelper是否有缓存如果有则直接使用// 2. 如果没有则反射创建// 3. 创建完成调用start()// 4. 判断是否系统启动完成如果完成则调用onBootCompleted()// 5. 将启动的服务加入DumpManager中以便bugreport触发其dumpfinal int N services.length;for (int i 0; i N; i) {String clsName services[i];if (DEBUG) Log.d(TAG, loading: clsName);log.traceBegin(metricsPrefix clsName);long ti System.currentTimeMillis();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]);mServices[i].start();log.traceEnd();// Warn if initialization of component takes too longti System.currentTimeMillis() - ti;if (ti 1000) {Log.w(TAG, Initialization of clsName took ti ms);}if (mBootCompleteCache.isBootComplete()) {mServices[i].onBootCompleted();}dumpManager.registerDumpable(mServices[i].getClass().getName(), mServices[i]);}mSysUIComponent.getInitController().executePostInitTasks();log.traceEnd();mServicesStarted true;
}从上面可以看到SystemUIService主要功能就是去调用各个服务的start()和onBootCompleted()功能。完成启动。
现在请思考为什么这部分内容要放在SystemUIApplication中就不能直接放在SystemUIService中吗
在回答这个问题之前我们先来看看getSystemUIServiceComponents()函数如何得到需要启动的服务的
getSystemUIServiceComponents函数获取欲启动的服务
源码如下
public String[] getSystemUIServiceComponents(Resources resources) {return resources.getStringArray(R.array.config_systemUIServiceComponents);
}上面的函数通过配置的字符串数组获得可能大家就会好奇了——为什么不同Dagger2呢这么方便的东西怎么还配置个字符串数组呀。
为什么这么配置我想主要有如下的几个原因
AOSP代码的编译可以使用override功能将这部分资源文件放在自定义的目录下达到修改的目的历史原因以前就是这么配置的哈哈
现在我们来思考启动服务的功能逻辑为什么就不能放在SystemUIservice中而要放在SystemUIApplication中。 如果要放在SystemUIApplication中为什么不通过SystemUIApplication来启动而要SystemUIService来启动。 除了SystemUIService启动服务以外在多用户的情况下也需要启动一些服务而此时SystemUI应用 先调用SystemUIApplication,而不会调用SystemUIService。因为SystemUIService的触发是由system_server启动的。 加上监听系统的启动逻辑需要统一处理将启动逻辑放入SystemUIApplication变得理所当然。 注意显然这就导致一个bug倘若SystemUI中途报错停止运行当其再次运行的时候由SystemUIService启动的各个 服务还能够正确的初始化吗显然不能这在我写这边文章过程中经常出现 既然启动逻辑已经放入了SystemUIApplication中那么由SystemUIApplication来启动这部分服务不可以吗 为什么要单独一个SystemUIService作为入口进行启动呢要回答这个问题就需要知道Android的应用启动是通过启动某个待运行的组件。即system_server若要运行SystemUI,必然要启动某个组件而不能只启动Application。 故此我们需要一个用来启动的组件这个组件就是SystemUI. 由它负责各个具体服务的启动。加上对开机广播的监听多用户下的协作就将这部分内容放在了SystemUIApplication中完成。 又因为SystemUIService需要依赖注入所以创建了SystemUIAppComponentFactory来实现对应的依赖注入。
至此我们已经完全弄清了SystemUIService,SystemUIApplication,SystemUIAppComponentFactory的启动流程 以及为什么这么分配功能。现总结如下。
system_server启动之后开始启动各种服务在启动其他服务的时候会先通过PackageManager获得要启动systemui的组件名字然后根据名字启动systemui组件在上面一步获得的名字就是SystemUIService。启动SystemUIService则会先创建SystemUIApplication在创建之前会先调用SystemUIAppComponentFactory添加相应的依赖注入SystemUIApplication创建之后则会监听系统的启动广播。接着创建SystemUIService,再创建之前还会先调用SystemUIAppComponentFactory相应的方法添加依赖注入创建SystemUIService之后通过SystemUIApplication启动各种服务
至此整个SystemUI启动完成。
给SystemUI添加一个自定义的服务
有了前面的分析我们现在需要进行测试一下写一个自定义服务的模块在这个模块中我们仅仅打印出其启动过程即可。
新建一个类叫做PrintLogService这个类继承SystemUI即可。如下
public class PrintLogService extends SystemUI{private String TAG PrintLogService;//使用Inject标记让Dagger2自动管理依赖Injectpublic PrintLogService(Context context) {super(context);}//简单打印logOverridepublic void start() {Slog.d(TAG, Start PrintLogService);}//简单打印logOverrideprotected void onBootCompleted() {Slog.d(TAG,PrintLogService boot completed);}}将这个类的名字放入配置数组中即config_systemUIServiceComponents如下
!-- 最后一行加入我们自定义的服务 --string-array nameconfig_systemUIServiceComponents translatablefalseitemcom.android.systemui.util.NotificationChannels/itemitemcom.android.systemui.keyguard.KeyguardViewMediator/itemitemcom.android.systemui.recents.Recents/itemitemcom.android.systemui.volume.VolumeUI/itemitemcom.android.systemui.statusbar.phone.StatusBar/itemitemcom.android.systemui.usb.StorageNotification/itemitemcom.android.systemui.power.PowerUI/itemitemcom.android.systemui.media.RingtonePlayer/itemitemcom.android.systemui.keyboard.KeyboardUI/itemitemcom.android.systemui.shortcut.ShortcutKeyDispatcher/itemitemstring/config_systemUIVendorServiceComponent/itemitemcom.android.systemui.util.leak.GarbageMonitor$Service/itemitemcom.android.systemui.LatencyTester/itemitemcom.android.systemui.globalactions.GlobalActionsComponent/itemitemcom.android.systemui.ScreenDecorations/itemitemcom.android.systemui.biometrics.AuthController/itemitemcom.android.systemui.SliceBroadcastRelayHandler/itemitemcom.android.systemui.statusbar.notification.InstantAppNotifier/itemitemcom.android.systemui.theme.ThemeOverlayController/itemitemcom.android.systemui.accessibility.WindowMagnification/itemitemcom.android.systemui.accessibility.SystemActions/itemitemcom.android.systemui.toast.ToastUI/itemitemcom.android.systemui.wmshell.WMShell/itemitemcom.android.systemui.PrintLogService/item/string-array
使用如下的命令进行编译并push到手机中
mmm frameworks/base/packages/SystemUI
adb root
adb remount
adb shell rm -rf system_ext/priv-app/SystemUI
adb push out/**/system_ext/priv-app/SystemUI /system_ext/priv-app/然后使用kill杀死现有SystemUI进程即可
从log中我们可以看到如下的输出 这表示我们自定义的服务启动成功
本文完
在文中,我们仅仅对于mContextAvailableCallback.onContextAvailable(this);一笔带过 这里面是关于Dagger2中各种Component的初始化下一篇文章将会从这个函数出发一探SystemUI中各种Component的初始化理解SystemUI中各个组件应该怎样被使用。