Android 12 源碼分析 —— 應(yīng)用層 二(SystemUI大體組織和啟動(dòng)過程)
在前一篇文章中,我們介紹了SystemUI怎么使用IDE進(jìn)行編輯和調(diào)試。這是分析SystemUI的最基礎(chǔ),希望讀者能盡量掌握。
本篇文章,將會(huì)介紹SystemUI的大概組織架構(gòu),以及它的啟動(dòng)過程。本篇文章讀完,將會(huì)知道:
- SystemUI為什么選擇使用Dagger2
- SystemUI怎么新建一個(gè)模塊
- SystemUI的啟動(dòng)流程
在進(jìn)行閱讀之前,請跟著我思考如下的問題:
- SystemUI要完成哪些功能?
- 各個(gè)功能之間需要溝通嗎?
- 倘若各個(gè)功能之間需要進(jìn)行溝通,怎樣組織他們之間的引用關(guān)系
- 各個(gè)功能需要與系統(tǒng)服務(wù)溝通嗎?
- 倘若各個(gè)功能需要與系統(tǒng)服務(wù)溝通,怎么高效的組織他們之間的引用
- 各個(gè)功能之間有依賴關(guān)系嗎?
- 各個(gè)功能之間啟動(dòng)有順序要求嗎?
針對(duì)上述1,2,3問題,我們可以從界面上看到,SystemUI包含:鎖屏,狀態(tài)欄,導(dǎo)航欄,Toast的顯示,音量調(diào)節(jié)等等功能。這些功能之間可能會(huì)相互引用,如狀態(tài)欄模塊需要知道鎖屏模塊的情況,便于決定是否要隱藏一些狀態(tài)欄圖標(biāo)。
這些功能多多少少會(huì)與系統(tǒng)之間進(jìn)行交互,如,鎖屏模塊需要知道電源按鈕的情況,便于決定是否要顯示或者隱藏鎖屏;再如,各個(gè)功能模塊是否要顯示夜間模式等等。
從這里我們已經(jīng)知道,各個(gè)模塊之間需要相互引用,且與系統(tǒng)之間也會(huì)有溝通,為此,需要設(shè)計(jì)好架構(gòu),讓各個(gè)模塊便于獲得想要的對(duì)象,而這些對(duì)象的創(chuàng)建,可能又依賴于其他對(duì)象的創(chuàng)建。事實(shí)上SystemUI的各個(gè)對(duì)象之間依賴關(guān)系比較復(fù)雜,如果手動(dòng)創(chuàng)建各個(gè)對(duì)象,需要寫非常多的代碼。為此,我們使用新的組件管理方式:DI(依賴注入)
依賴注入的核心思想就是:在編寫代碼的時(shí)候,不再由開發(fā)人員,顯示的去編輯各個(gè)對(duì)象應(yīng)該怎么創(chuàng)建,而是由依賴注入庫去決定怎么創(chuàng)建對(duì)象。SystemUI選擇了Dagger2作為其依賴注入庫。
注意:這里需要說明什么叫做依賴?依賴就是一個(gè)組件A在完成功能的過程中,需要用到另外一個(gè)組件B,則叫做A依賴B。A,B可以是類,對(duì)象,模塊等等。那么什么叫做注入呢?注入就是將依賴項(xiàng)提供給組件的過程。
現(xiàn)在設(shè)計(jì)好了各個(gè)模塊之間怎么得到想要的對(duì)象——Dagger依賴注入。
下面想一想SystemUI和系統(tǒng)之間該怎么去溝通呢?這就涉及到應(yīng)該怎么劃分SystemUI,SystemUI作為整個(gè)系統(tǒng)的基礎(chǔ)UI組成,在整個(gè)Android 系統(tǒng)中,占據(jù)非常重大的地位。為了能夠適應(yīng)更加多元的場景如Android TV,Android Car他們可能又不一樣的SystemUI,同時(shí)又為了滿足模塊化設(shè)計(jì)。故將SystemUI設(shè)計(jì)成一個(gè)常駐內(nèi)存中的apk。
既然已經(jīng)將SystemUI設(shè)計(jì)成了一個(gè)apk,那么它就單獨(dú)運(yùn)行在另外一個(gè)進(jìn)程中,要與系統(tǒng)進(jìn)行溝通,那么只有通過Android的四大組件進(jìn)行交互,以及使用Android的Binder進(jìn)行交互。那么SystemUI就變成了各種Service,Activity,BroadcastReceiver,ContentProvider的一個(gè)集合了。然后在合適的時(shí)候啟用這些組件。
結(jié)合前面的思考,各個(gè)功能模塊,如鎖屏,狀態(tài)欄,導(dǎo)航欄只要放入合適的組件之中,就可以在正確的地方顯示了。同時(shí)為了方便訪問其他各個(gè)模塊,我們還使用了Dagger進(jìn)行輔助。
看上去似乎一切美好,但是真的美好嗎?是否漏掉了一個(gè)重要問題?這些模塊之間有先后順序嗎?為了能快速的進(jìn)入系統(tǒng),目前SystemUI不做這方面的要求,如果某一個(gè)模塊需要等待另外一個(gè)模塊準(zhǔn)備好之后,才能正常工作,那么就需要調(diào)整設(shè)計(jì)邏輯了。
查看SystemUI的組件有哪些
從上面我們知道,SystemUI整體上被放入了四大組件之中,那么我們查看AndroidManifest.xml看看都有哪些組件被定義了。
在android-12.0.0_r34分支上,AndroidManifest.xml有38個(gè)Activity,11個(gè)Service,4個(gè)provider,11個(gè)receiver
接下來,我將給出各個(gè)組件的簡單描述,再后續(xù)的章節(jié)中,我們將會(huì)細(xì)細(xì)介紹這些組件如何啟動(dòng),完成哪些功能。
Activity篇
- LongScreenShotActivity:長截圖使用的視圖,用戶決定長截圖時(shí),調(diào)起這個(gè)Activity。
- ScreenRecordDialog:錄屏?xí)r彈出的選項(xiàng)框視圖。
- TunerActivity:這是給研發(fā)人員用的微調(diào)界面??梢允褂孟旅婷畲蜷_界面入口
adb shell pm enable com.android.systemui/com.android.systemui.tuner.TunerActivity
然后在設(shè)置->系統(tǒng)->System UI Tuner進(jìn)入界面
- DemoMode:SystemUI的Demo模式,也是給研發(fā)人員用的,他是TunerActivity的功能補(bǔ)充,可在開發(fā)者選項(xiàng)中打開
- ForceReSizableInfoActivity:彈出應(yīng)用無法在分屏模式,或者輔助屏幕下運(yùn)行
- UsbPermissionActivity:確定USB權(quán)限彈框
- UsbResolverActivity:為USB設(shè)備選擇一個(gè)應(yīng)用彈框
- UsbConfirmActivity:彈出一個(gè)視圖,用來確定是否要使用某個(gè)app,是UsbResolverActivity的后續(xù)視圖
- SensorUseStartedActivity:當(dāng)傳感器在privacy mode下,欲使用傳感器時(shí)的彈框
- TvUnblockSensorActivity:同SensorUseStartedActivity,只不過這個(gè)是運(yùn)用在電視機(jī)上的視圖
- UsbAccessoryUriActivity:彈出一個(gè)框,讓你去下載這個(gè)USB設(shè)備對(duì)應(yīng)的應(yīng)用
- UsbContaminantActivity:彈出一個(gè)框,表示USB已經(jīng)停用,停用的原因可能是usb端口處有贓物等。
- UsbDebuggingActivity:彈出是否允許USB調(diào)試
- UsbDebuggingActivityAlias:這個(gè)是UsbDebuggingActivity的別名
- WifiDebuggingActivity:彈出是否允許網(wǎng)絡(luò)進(jìn)行無線調(diào)試
- WifiDebuggingActivityAlias:是WifiDebuggingActivity的別名
- WifiDebuggingSecondaryUserActivity:彈出目前登錄的用戶無法開啟無線調(diào)試功能,需要切換為主用戶
- NetworkOverLimitActivity:彈出數(shù)據(jù)流量已經(jīng)達(dá)到上限
- MediaProjectionPermissionActivity:多媒體投屏權(quán)限確認(rèn)
- TvNotificationPanelActivity:TV專用,彈出一個(gè)消息框
- SlicePermissionActivity:Slice權(quán)限彈框
- DessertCase:彩蛋之一
- MLandActivity:彩蛋小游戲
- PeopleSpaceActivity:提示Pepole Space UI的位置,android 11新增功能
- LaunchConversationActivity:當(dāng)會(huì)話被點(diǎn)擊的時(shí)候,展開視圖,Android 11 新增功能
- WorkLockActivity:解鎖work profile的界面
- CreateUserActivity:創(chuàng)建用戶視圖
- Somnambulator:屏保
- BrightnessDialog:亮度彈框
- ForegroundServicesDialog:展示前臺(tái)services的一個(gè)彈框
- ChooserActivity:彈出一個(gè)框,讓用戶選擇打開哪一個(gè)應(yīng)用,來處理當(dāng)前的Intent
- ControlsProviderSelectorActivity 彈出“選擇要添加控制器的應(yīng)用”
- ControlsEditingActivity:編輯控制器,拖拽進(jìn)行編輯
- ControlsFavoritingActivity:控制器,偏好設(shè)置
- ControlsActivity:列出設(shè)備控制器
- WalletActivity:電子錢包
- ControlsRequestDialog:control請求添加設(shè)備控制器彈框
注意:這里的Controls,是外部設(shè)備的控制器,如全屋智能中的控制器。
上面只是一個(gè)非常簡單的概覽,而一些常見組件邏輯和UI細(xì)節(jié),將會(huì)在后續(xù)文章中出現(xiàn)。
看到這里,可能會(huì)有讀者提問:上面的Activity似乎沒有狀態(tài)欄和鎖屏呀,他們的視圖,難道不在這些Activity里面嗎?
欲探討這個(gè)問題,還需要先看剩下的組件。
Services篇
- SystemUIService:哇哦,多么讓人提神的名字,這個(gè)Service包含了SystemUI內(nèi)部的大部分功能它也是我們SystemUI源碼分析的重中之重。
- SystemUISecondaryUserService:多用戶情況下,該service保證多用戶的SystemUI功能正常
- SystemUIAuxiliaryDumpService:開發(fā)使用,dump出各個(gè)必要部件的信息并查看
- TakeScreenshotService:截屏相關(guān)的service
- RecordingService:錄屏相關(guān)的service
- ImageWallpaper:壁紙相關(guān)的service
- PeopleBackupFollowUpJob:People service ui相關(guān)的服務(wù)
- DessertCaseDream:小彩蛋
- KeyguardService:鎖屏相關(guān)的服務(wù)
- AuxiliaryPersistenceWrapper$DeletionJobService:外部設(shè)備控制器相關(guān)的服務(wù)
- DozeService:跟Doze相關(guān)的服務(wù)
ContentProvider篇
- FileProvider:提供文件
- KeyguardSliceProvider:提供鎖屏Slice
- ClockOptionsProvider:為選擇器程序提供時(shí)鐘預(yù)覽
- PeopleProvider:返回給定快捷方式的 People Tile 預(yù)覽
BroadcastReceiver篇
- ScreenshotServiceErrorReceiver:截屏失敗廣播接收器
- SysuiRestartReceiver:重啟SystemUI廣播接收器
- ActionProxyReceiver:攔截share和edit intent,便于提前處理一些事情的廣播接收器
- DeleteScreenshotReceiver:刪除截屏廣播接收器
- SmartActionsReceiver:用戶點(diǎn)擊通知中的smart action之后,用于接收對(duì)應(yīng)的廣播,并執(zhí)行smart action
- ControlsRequestReciver:接收增加控制器請求的廣播接收器
- TunerService$ClearReciver:用于調(diào)用TunerService的clear的廣播接收器
- KeyboardShortcutsReceiver:展示或者隱藏鍵盤快捷鍵的廣播接收器
- MediaOutputDialogReceiver:接收媒體輸出Intent的廣播接收器
- PeopleSpaceWidgetPinnedReceiver:當(dāng)一個(gè)聯(lián)系人Tile widget被添加之后,這個(gè)接收器被調(diào)用
- PeopleSpaceWidgetProvider:People Space widget 的實(shí)現(xiàn)
閱讀到這個(gè)地方,讀者依然會(huì)有疑問——SystemUI的鎖屏和狀態(tài)欄,到底在什么地方顯示出來的?Service里面能顯示UI嗎?明顯不合理呀。那么安卓的鎖屏和狀態(tài)欄,到底是怎么顯示出來的呢?
小提示:SystemUI除了上面列出的組件來顯示視圖以外,還通過直接與WindowManager交互來顯示視圖。What~~ 是不是會(huì)感嘆一句,android的設(shè)計(jì)架構(gòu)還真是有些混亂。
此處不表,后續(xù)詳解。接下來我們需要先處理前面提到的關(guān)于Dagger2,它是如何處理SystemUI中各個(gè)組件的引用關(guān)系的。
SystemUI內(nèi)部組件設(shè)計(jì)
我們要讓Dagger2來管理各個(gè)組件的依賴關(guān)系,那我我們必然要告訴Dagger2有怎樣的依賴關(guān)系,應(yīng)該使用什么樣的方式呢?用xml文件來描述嗎?還是用其他的方式呢?
Dagger2使用了java的注解來描述他們之間的依賴關(guān)系。同時(shí)為了提升性能,Dagger2會(huì)在編譯的時(shí)候,根據(jù)注解生成不同的java對(duì)象,然后在生成的java對(duì)象中,安排好了一切的依賴,以及生命周期。
用來表示各種依賴關(guān)系的注解,叫做給Dagger2畫一副圖(graph).接下來的我們結(jié)合SystemUI中的實(shí)例,看看SystemUI如何給Dagger2畫了一副圖。
Dagger2 在SystemUI中的應(yīng)用
在我們的設(shè)想中,需要一個(gè)最最頂部的對(duì)象比如RootManager.然后根據(jù)這個(gè)RootManager來獲得我們需要的各個(gè)對(duì)象。
在SystemUI中,依然也有這么個(gè)RootManager。它就是:GlobalRootComponent。SystemUI的各個(gè)模塊,想要拿到自己想要的對(duì)象,可以通過GlobalRootComponent獲取。
注意:讀者,看到這里,肯定會(huì)非常疑惑,為什么要叫Component,而不是Manager,畢竟Manager在Android中多么的常見。這是因?yàn)镾ystemUI使用了Dagger2的抽象。在Dagger2中,Component表示一個(gè)組件,事實(shí)上它是一個(gè)容器,里面包含有它可以提供的所有依賴。故此GlobalRootComponent則是可以提供給所有依賴的一個(gè)組件。
那么我們來看看如何給GlobalRootComponent畫圖的。
//@Singeton:告訴Dagger2所有帶有@singtone注解的對(duì)象,生命周期一致。此處表示全局唯一
//@Component(xxx):告訴Dagger2,定義了一個(gè)Component組件
//modules={xxx}:告訴Dagger2,這個(gè)組件依賴這些模塊。關(guān)于模塊的概念,見后文
@Singleton
@Component(modules = {
GlobalModule.class,
SysUISubcomponentModule.class,
WMModule.class})
//此處是interface接口定義,Dagger2會(huì)生成對(duì)應(yīng)的實(shí)現(xiàn)類,并按照我們給Dagger2的注解圖,管理好
//各個(gè)對(duì)象的依賴和創(chuàng)建
public interface GlobalRootComponent {
//@Component.Builder:
//告訴Dagger2,這個(gè)是創(chuàng)建GlobalRootComponent的Builder類
//請務(wù)必要思考:為什么此處的對(duì)象創(chuàng)建要用Builder模式,而不是工廠模式?
@Component.Builder
interface Builder {
@BindsInstance
Builder context(Context context);
GlobalRootComponent build();
}
//提供一個(gè)方法,這個(gè)方法的返回類型,就是這個(gè)組件可以提供的依賴,此處表示可以提供
//WMComponent.Builder對(duì)象
WMComponent.Builder getWMComponentBuilder();
//表示此組件可以提供,SysUIComponent.Builder對(duì)象
SysUIComponent.Builder getSysUIComponent();
//注:上面兩個(gè)方法提供的返回類型可以看到,他們依然是一個(gè)Component
//表示可以提供ThreadFactory對(duì)象
ThreadFactory createThreadFactory();
}
在上面的例子中,我們提到了模塊module這個(gè)概念,在介紹這個(gè)概念之前,先思考一個(gè)問題:如果GlobalRootComponent中有很多很多依賴怎么辦呢?如果每個(gè)都畫在圖上,就會(huì)顯得雜亂無章,因此dagger2提供一種功能,將這些不同的依賴用Module進(jìn)行邏輯上面的劃分。然后只需要在給Component畫圖時(shí),進(jìn)行如下指定即可:
@Component(modules={xxx.class})
我們選取SysUISubComponentModule進(jìn)行查看,源碼如下:
//@Module:告訴Dagger2,這個(gè)module內(nèi)的所有依賴,邏輯劃分為:SysUISubcomponentModule
//subcomponents = {SysUIComponent.class}:告訴Dagger2,這個(gè)模塊含有SysUIComponent子組件
//關(guān)于子組件的概念,我們下文介紹
@Module(subcomponents = {SysUIComponent.class})
public abstract class SysUISubcomponentModule {
}
在上面的例子中,我們提到了subcomponent,在介紹這個(gè)概念之前,我們再來想個(gè)問題:如果一個(gè)Component提供一個(gè)對(duì)象給其他使用者,被提供的對(duì)象,應(yīng)該是每次都創(chuàng)建,還是只創(chuàng)建一次呢?這就涉及到對(duì)象的生命周期,為了能夠更好的管理生命周期,我們建議,同屬于一個(gè)生命周期的對(duì)象,放在一個(gè)子組件中。因此,上面的SysUIComponent子組件中所有對(duì)象,將屬于同一個(gè)生命周期。當(dāng)然subcomponent也不僅僅可以隔離生命周期,還可以隔離模塊使代碼更加清晰
那么我們看看這個(gè)subcomponent是怎么被告知Dagger2的。
//@SysUISingleton:告訴Dagger2所有SysUISingleton注解的對(duì)象生命周期相同
//@Subcomponent:告訴Dagger2,這是一個(gè)子組件
//modules={xxx}:告訴dagger2,這個(gè)子組件有這么些module的需要依賴
@SysUISingleton
@Subcomponent(modules = {
DefaultComponentBinder.class,
DependencyProvider.class,
SystemUIBinder.class,
SystemUIModule.class,
SystemUIDefaultModule.class})
public interface SysUIComponent {
//告訴Dagger2生命周期
@SysUISingleton
//告訴Dagger2這個(gè)子組件的Builder接口定義
@Subcomponent.Builder
interface Builder {
//省略若干相同部分
//@BindsInstance:告訴Dagger2,將t綁定到這個(gè)Builder對(duì)象中
//在Dagger2根據(jù)我們畫的圖,會(huì)根據(jù)這個(gè)Builder接口,生成一個(gè)SysUIComponentBuilder對(duì)象
//在這個(gè)對(duì)象中,會(huì)有一個(gè)成員,類型為Optional<TaskSurfaceHelper>名字為setTaskSurfaceHelper.
//然后這個(gè)setTaskSurfaceHelper()接口函數(shù)的實(shí)現(xiàn),就會(huì)將參數(shù)傳入的t保存在setTaskSurfaceHelper成員中。
//這個(gè)過程就叫做:綁定實(shí)例,也即@BindsInstance的語義
@BindsInstance
Builder setTaskSurfaceHelper(Optional<TaskSurfaceHelper> t);
//任何一個(gè)Builder接口,都必須有一個(gè)build()函數(shù),且返回類型為需要構(gòu)建的對(duì)象類型,此處即為SysUIComponent
SysUIComponent build();
}
//定義了一個(gè)默認(rèn)方法,這個(gè)方法什么也沒有做
default void init() {
// Do nothing
}
//告訴Dagger2它的生命周期
@SysUISingleton
//subcomponent和component一樣,如果想要對(duì)外提供依賴,就可以定義任何一個(gè)函數(shù),函數(shù)的返回類型就是
//被提供對(duì)象的類型。
BootCompleteCacheImpl provideBootCacheImpl();
//省略若干相同部分
//當(dāng)返回類型為空,而傳入類型不為空的時(shí)候,表示需要向傳入類型對(duì)象(SystemUIAppComponentFactory)
//中被@inject標(biāo)記的成員賦值,叫做注入
//理論上,函數(shù)名為任意值,但是此種函數(shù),幾乎只會(huì)完成注入的功能,因此此函數(shù)最后都叫做inject
void inject(SystemUIAppComponentFactory factory);
//省略若干相同部分
}
在上面的inject函數(shù)中,我們可以看看SystemUIAppComponentFactory是怎么樣的,源碼如下:
public class SystemUIAppComponentFactory extends AppComponentFactory {
//@Inject:告訴Dagger2,這個(gè)成員,需要Dagger2的注入。
//可是Dagger2又是如何知道,該怎么創(chuàng)建ContextComponentHelper的呢?
//這就是我們給Dagger2畫圖的作用,我們已經(jīng)提前畫好圖給Dagger2,告訴它應(yīng)該
//怎么創(chuàng)建這個(gè)ContextComponentHelper
@Inject
public ContextComponentHelper mComponentHelper;
}
接下來,看看我們是怎么給ContextComponentHelper畫圖的,源碼如下:
public interface ContextComponentHelper {
//省略若干無用部分
}
從上面的源碼可以知道,它沒有任何注解,即它沒有被畫如Dagger2的圖中,被畫入Dagger2圖的另有它類,ContextComponentHelper的實(shí)現(xiàn)類為:ContextComponentResolver,源碼如下:
//@SysUISingleton:告訴Dagger2它的生命周期
@SysUISingleton
public class ContextComponentResolver implements ContextComponentHelper {
private final Map<Class<?>, Provider<Activity>> mActivityCreators;
private final Map<Class<?>, Provider<Service>> mServiceCreators;
private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
private final Map<Class<?>, Provider<RecentsImplementation>> mRecentsCreators;
private final Map<Class<?>, Provider<BroadcastReceiver>> mBroadcastReceiverCreators;
//@Inject:此處就是告訴Dagger圖,注入Dagger2的各種輔助功能,幫助創(chuàng)建這個(gè)對(duì)象
//在創(chuàng)建對(duì)象的時(shí)候,需要它的各種參數(shù),而這些參數(shù)又應(yīng)該怎么被Dagger2提供呢?
//只要我們把需要的參數(shù),畫好圖給Dagger2即可,過程就和這個(gè)ContextComponentResolver一樣啦,
//在構(gòu)造器上面標(biāo)注一下@Inject就可以了
@Inject
ContextComponentResolver(Map<Class<?>, Provider<Activity>> activityCreators,
Map<Class<?>, Provider<Service>> serviceCreators,
Map<Class<?>, Provider<SystemUI>> systemUICreators,
Map<Class<?>, Provider<RecentsImplementation>> recentsCreators,
Map<Class<?>, Provider<BroadcastReceiver>> broadcastReceiverCreators) {
mActivityCreators = activityCreators;
mServiceCreators = serviceCreators;
mSystemUICreators = systemUICreators;
mRecentsCreators = recentsCreators;
mBroadcastReceiverCreators = broadcastReceiverCreators;
}
//省略若干無用部分
}
看到此處,我們已經(jīng)大體知道了Dagger2該怎么使用(該怎么提供一個(gè)圖給它)。但是仔細(xì)思考依然會(huì)有一個(gè)問題——要是某些類,不是由我們創(chuàng)建,那么我們就沒法在構(gòu)造器上面加上@Inject了,那么怎么才能讓Dagger2知道:當(dāng)它需要這個(gè)對(duì)象的時(shí)候,應(yīng)該怎么創(chuàng)建呢?此時(shí)Dagger2提供了另外一個(gè)注解@Provides .
我們以GlobalModule為例進(jìn)行說明,源碼如下:
//@Module:告訴Dagger2,定義一個(gè)邏輯模塊,這個(gè)模塊包含F(xiàn)rameworkServicesModule,GlobalConcurrencyModule
@Module(includes = {
FrameworkServicesModule.class,
GlobalConcurrencyModule.class})
public class GlobalModule {
//@Provides:告訴Dagger2,如果需要DisplayMetrics對(duì)象,就調(diào)用provideDisplayMetrics()函數(shù)即可
//至于這個(gè)函數(shù)需要的參數(shù)Context,該怎么創(chuàng)建,Dagger2已經(jīng)能夠從我們給它的圖中自動(dòng)找到了
@Provides
public DisplayMetrics provideDisplayMetrics(Context context) {
DisplayMetrics displayMetrics = new DisplayMetrics();
context.getDisplay().getMetrics(displayMetrics);
return displayMetrics;
}
}
至此,我們已經(jīng)大體介紹完了Dagger2中如何畫圖(即怎么使用注解),如,怎么使用@Component,@SubComponent,@Inject,@Provides,@Module等等,Dagger2中還有其他注解沒有引入,但是這已經(jīng)足夠本文接下來的閱讀了。關(guān)于其他注解的內(nèi)容,可直接參考Dagger2的文檔,本文只專注于SystemUI的分析
可是上面只是畫圖,并沒有提到怎么使用的,接下來,我們將結(jié)合SystemUI的啟動(dòng)過程,看看怎么使用上面畫出來的Dagger2的圖。
SystemUI的啟動(dòng)過程
任何一個(gè)Apk的啟動(dòng),都是從它的四大組件開始啟動(dòng),而在四大組件,開始啟動(dòng)之前,會(huì)去查看是否有自定義的Application,如果有,則會(huì)先創(chuàng)建Application。
從Android 9開始,增加了一個(gè)AppComponentFactory用來在創(chuàng)建四大組件之前,進(jìn)行相應(yīng)的操作。它同Application一樣,被配置在了AndroidManifest.xml中,如下:
<application
android:name=".SystemUIApplication"
.
.
.
android:appComponentFactory=".SystemUIAppComponentFactory">
<!--省略若干不相干話題-->
</application>
從這個(gè)配置中我們可以看到如下的啟動(dòng)過程:
SystemUIAppComponentFactory->SystemUIApplication->某個(gè)欲啟動(dòng)的組件(Android四大組件)。
在進(jìn)一步分析這個(gè)流程之前,先來看看誰啟動(dòng)了SystemUI
system_server的出發(fā)點(diǎn)
可是有讀者會(huì)問:SystemUI到底是由誰啟動(dòng)的呢?你看其他的app都是通過點(diǎn)擊圖標(biāo)啟動(dòng),SystemUI是由誰啟動(dòng)的?
正確答案:SystemUI由Android系統(tǒng)中,一個(gè)叫做sytstem_server的進(jìn)程啟動(dòng),system_server在開機(jī)的時(shí)候啟動(dòng),然后由system_server啟動(dòng)各種關(guān)鍵服務(wù),其中就包括啟動(dòng)SystemUI.這在后面分析system_server時(shí),會(huì)詳細(xì)講解.
此處只給出system_server啟動(dòng)SystemUI的簡略說明:
- 系統(tǒng)啟動(dòng)system_server進(jìn)程之后,會(huì)執(zhí)行
new SystemServer().run();
- 在run()方法中,會(huì)去啟動(dòng)各種服務(wù),這些服務(wù)包括:
- 啟動(dòng)引導(dǎo)服務(wù)
- 核心服務(wù)
- 其他服務(wù)
-
在啟動(dòng)其他服務(wù)的時(shí)候,會(huì)去啟動(dòng)SystemUI(通過Intent)。而要獲得啟動(dòng)SystemUI的具體組件,就通過
Android的PackageManager得到, -
而PacakgeManager則通過讀取配置config_systemUIServiceComponent得到具體的組件名。Android系統(tǒng)中這個(gè)配置
為
<string name="config_systemUIServiceComponent" translatable="false"
>com.android.systemui/com.android.systemui.SystemUIService</string>
可見這正是,我們在SystemUI中定義的組件。
那么我們就可以總結(jié)一下SystemUI的啟動(dòng)過程了
- Android系統(tǒng)啟動(dòng)完成,啟動(dòng)system_server
- system_server,根據(jù)配置,通過Intent來啟動(dòng)SystemUI的組件
- SystemUI在啟動(dòng)組件之前,會(huì)先創(chuàng)建SystemUIAppComponentFactory對(duì)象,然后調(diào)用其相應(yīng)方法
- 接著,SystemUI會(huì)創(chuàng)建SystemUIApplication,然后調(diào)用其相應(yīng)方法
- 最后,SystemUI會(huì)創(chuàng)建SystemUIService,并調(diào)用相應(yīng)方法,在創(chuàng)建SystemUIService之前,則又會(huì)調(diào)用在第3步中創(chuàng)建的SystemUIAppComponentFactory對(duì)象的相應(yīng)方法
為何要使用SystemUIAppComponentFactory
SystemUIAppComponentFactory源碼如下:
public class SystemUIAppComponentFactory extends AppComponentFactory {
private static final String TAG = "AppComponentFactory";
@Inject
public ContextComponentHelper mComponentHelper;
public SystemUIAppComponentFactory() {
super();
}
@NonNull
@Override
//在創(chuàng)建Application之前,這個(gè)函數(shù)被調(diào)用
public Application instantiateApplicationCompat(
@NonNull ClassLoader cl, @NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
//調(diào)用父類方法,創(chuàng)建Application,此處會(huì)創(chuàng)建AndroidManifest.xml中配置的類
//也即SystemUIApplication
Application app = super.instantiateApplicationCompat(cl, className);
//倘若創(chuàng)建的組件是ContextInitializer,則注冊一個(gè)回調(diào)
//請一定注意:雖然此處創(chuàng)建了Application,但是它還不能當(dāng)做Context來使用
if (app instanceof ContextInitializer) {
((ContextInitializer) app).setContextAvailableCallback(
context -> {
//1.在回調(diào)中,首先創(chuàng)建SystemUIFactory對(duì)象
SystemUIFactory.createFromConfig(context);
//2.通過這個(gè)SystemUIFactory得到SysUIComponent
//3.注入SystemUIAppComponentFactory中的成員,見上一小節(jié)
SystemUIFactory.getInstance().getSysUIComponent().inject(
SystemUIAppComponentFactory.this);
}
);
}
return app;
}
@NonNull
@Override
//ContentProvider被創(chuàng)建之前,該函數(shù)被回調(diào)
public ContentProvider instantiateProviderCompat(
@NonNull ClassLoader cl, @NonNull String className)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
//省略若干
//此處沒有列出內(nèi)容,原因是:它的邏輯和上一個(gè)函數(shù)一樣
}
@NonNull
@Override
public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className,
@Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
//省略若干
//此處沒有列出內(nèi)容,原因是:它的邏輯和上一個(gè)函數(shù)一樣
}
@NonNull
@Override
public Service instantiateServiceCompat(
@NonNull ClassLoader cl, @NonNull String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
//判斷是否為空,如果是,則再次注入
//第一次注入,在instantiateApplicationCompat()函數(shù)設(shè)置的回調(diào)中,
//這個(gè)回調(diào)由SystemUIApplication的onCreate()觸發(fā)
if (mComponentHelper == null) {
// This shouldn't happen, but does when a device is freshly formatted.
// Bug filed against framework to take a look: http://b/141008541
SystemUIFactory.getInstance().getSysUIComponent().inject(
SystemUIAppComponentFactory.this);
}
//注意:這里的Service的創(chuàng)建
//1. 先查詢mComponentHelper中是否有對(duì)應(yīng)的Service
//2. 如果有則直接用,如果沒有則調(diào)用父類方法創(chuàng)建
//對(duì)于SystemUIService而言,它的構(gòu)造函數(shù)有@Inject注解,因此當(dāng)調(diào)用mComponentHelper.resolveService時(shí),能夠正確返回SystemUIService
//請思考:為什么這個(gè)不要系統(tǒng)自己創(chuàng)建?
//答案:因?yàn)镾ystemUIService,需要有其他依賴對(duì)象,若是由系統(tǒng)創(chuàng)建,那么必然會(huì)有
//像SystemUIService.setXXX()之類的函數(shù),會(huì)增加代碼和邏輯。如果由Dagger2來創(chuàng)建則不會(huì)有
//這些煩惱
Service service = mComponentHelper.resolveService(className);
if (service != null) {
return service;
}
return super.instantiateServiceCompat(cl, className, intent);
}
@NonNull
@Override
public BroadcastReceiver instantiateReceiverCompat(@NonNull ClassLoader cl,
@NonNull String className, @Nullable Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
//省略若干
//此處沒有列出內(nèi)容,原因是:它的邏輯和上一個(gè)函數(shù)一樣
}
}
在這個(gè)類中,最最主要的功能就三點(diǎn):
- 相應(yīng)組件的創(chuàng)建,如SystemUIApplication,SystemUIService等
- mComponentHelper的初始化,也即通過注入實(shí)現(xiàn)
- 而在Service組件創(chuàng)建之前,可能會(huì)先查詢mComponentHelper中是否已經(jīng)有組件,若有則直接使用
在查看SystemUIApplication 和SystemUIService的創(chuàng)建之前,我們還是要再次思考一個(gè)問題:
為什么要用SystemUIAppComponentFactory這個(gè)類?這個(gè)類真的合理嗎?有更好的替代方案嗎?
我想答案就在SystemUIService的創(chuàng)建上。正是SystemUIAppComponentFactory這個(gè)類的使用,才讓我們更好的注入依賴
在進(jìn)入SystemUIService之前,最先創(chuàng)建的是SystemUIApplication。我們來看看它所做的工作。
SystemUIApplication
源碼如下:
public class SystemUIApplication extends Application implements
SystemUIAppComponentFactory.ContextInitializer {
public SystemUIApplication() {
super();
Log.v(TAG, "SystemUIApplication constructed.");
// SysUI may be building without protolog preprocessing in some cases
ProtoLog.REQUIRE_PROTOLOGTOOL = false;
}
@Override
public void onCreate() {
super.onCreate();
Log.v(TAG, "SystemUIApplication created.");
//用于跟蹤啟動(dòng)和關(guān)閉的時(shí)序數(shù)據(jù)
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
log.traceBegin("DependencyInjection");
//這就是初始化各種Dagger2依賴的地方,這個(gè)回調(diào)在SystemUIAppComponentFactory中被設(shè)置
mContextAvailableCallback.onContextAvailable(this);
//有了Dagger2,就是直接使用對(duì)應(yīng)的組件
mRootComponent = SystemUIFactory.getInstance().getRootComponent();
mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent();
mComponentHelper = mSysUIComponent.getContextComponentHelper();
mBootCompleteCache = mSysUIComponent.provideBootCacheImpl();
log.traceEnd();
//設(shè)置主題
setTheme(R.style.Theme_SystemUI);
//判斷是否為主進(jìn)程
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
//監(jiān)聽系統(tǒng)的啟動(dòng)廣播
IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
//設(shè)置線程渲染優(yōu)先級(jí)
int sfPriority = SurfaceControl.getGPUContextPriority();
Log.i(TAG, "Found SurfaceFlinger's GPU Priority: " + sfPriority);
if (sfPriority == ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_REALTIME_NV) {
Log.i(TAG, "Setting SysUI's GPU Context priority to: "
+ ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
ThreadedRendererCompat.setContextPriority(
ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
}
//注冊廣播接收器
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//mBootCompleteCache表示的是:是否SytemUI的各種服務(wù)啟動(dòng)完成
//這些服務(wù)的啟動(dòng),可能早于系統(tǒng)啟動(dòng)完成廣播,也可能晚于系統(tǒng)啟動(dòng)完成廣播
//1. 如果SystemUI的各種服務(wù)已經(jīng)啟動(dòng)完成則直接返回
if (mBootCompleteCache.isBootComplete()) return;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
//2. 如果沒有啟動(dòng)完成,則挨個(gè)啟動(dòng)
unregisterReceiver(this);
mBootCompleteCache.setBootComplete();
if (mServicesStarted) {
final int N = mServices.length;
for (int i = 0; i < N; i++) {
mServices[i].onBootCompleted();
}
}
}
}, bootCompletedFilter);
//監(jiān)聽是否Local改變
//如果Local改變,則通知中的顯示就需要改變,如中英文切換等
IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
if (!mBootCompleteCache.isBootComplete()) return;
// Update names of SystemUi notification channels
NotificationChannels.createAll(context);
}
}
}, localeChangedFilter);
} else {
//如果是子進(jìn)程則會(huì)進(jìn)入此部分邏輯
//如果是主用戶下的子進(jìn)程,則什么也不做,直接返回
String processName = ActivityThread.currentProcessName();
ApplicationInfo info = getApplicationInfo();
if (processName != null && processName.startsWith(info.processName + ":")) {
return;
}
//如果不是主用戶,則需要去啟動(dòng)必要的SystemUI組件
startSecondaryUserServicesIfNeeded();
}
}
//省略若干,簡單代碼
}
SystemUIApplication的代碼相對(duì)來講比較簡單,都已經(jīng)標(biāo)記在注釋里面了。接下來看看SystemUIService
SystemUIService
源碼如下:
public class SystemUIService extends Service {
//省略若干,簡單代碼
//@Inject:嘿嘿,這就是給Dagger畫的圖,好讓Dagger2知道怎么創(chuàng)建SystemUIService
@Inject
public SystemUIService(
@Main Handler mainHandler,
DumpHandler dumpHandler,
BroadcastDispatcher broadcastDispatcher,
LogBufferFreezer logBufferFreezer,
BatteryStateNotifier batteryStateNotifier) {
//省略賦值代碼
}
@Override
public void onCreate() {
super.onCreate();
//對(duì)沒錯(cuò),startServicesIfNeeded作為整個(gè)SystemUI關(guān)鍵服務(wù)的啟動(dòng)源頭,就在這里了。
//在進(jìn)入分析之前,先思考:為什么要放在這里執(zhí)行呢?就不能直接放在SystemUIApplication中嗎?
((SystemUIApplication) getApplication()).startServicesIfNeeded();
//LogBufferFreezer接收bugreport開始的廣播,然后停止對(duì)應(yīng)的LogBuffer的記錄
mLogBufferFreezer.attach(mBroadcastDispatcher);
//是否監(jiān)聽電池的狀態(tài),并且會(huì)提示在通知欄上
if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) {
mBatteryStateNotifier.startListening();
}
//調(diào)試代碼,用debug.crash_sysui觸發(fā)RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
throw new RuntimeException();
}
//Binder調(diào)試相關(guān)
//如果太多binder調(diào)用就觸發(fā)onLimitReached回調(diào)
if (Build.IS_DEBUGGABLE) {
//設(shè)置Binder代理計(jì)數(shù)開
BinderInternal.nSetBinderProxyCountEnabled(true);
//配置Binder代理觸發(fā)BinderProxyLimitListener回調(diào)的最高和最低閾值,最低表示:只有降到最低以下,才能再次觸發(fā)
BinderInternal.nSetBinderProxyCountWatermarks(1000,900);
//設(shè)置BinderProxyLimitListener監(jiān)聽
BinderInternal.setBinderProxyCountCallback(
new BinderInternal.BinderProxyLimitListener() {
@Override
public void onLimitReached(int uid) {
Slog.w(SystemUIApplication.TAG,
"uid " + uid + " sent too many Binder proxies to uid "
+ Process.myUid());
}
}, mMainHandler);
}
//啟動(dòng)DumpService,如果系統(tǒng)運(yùn)行bugreport,SystemUIAuxiliaryDumpService會(huì)將SystemUI中的一些關(guān)鍵數(shù)據(jù)dump出來
startServiceAsUser(
new Intent(getApplicationContext(), SystemUIAuxiliaryDumpService.class),
UserHandle.SYSTEM);
}
//省略若干,簡單代碼
}
接下來,我們看看startServicesIfNeeded()函數(shù)的具體內(nèi)容
startServicesIfNeeded()函數(shù)
該函數(shù)位于SystemUIApplication類內(nèi),源碼如下:
public void startServicesIfNeeded() {
//1. 獲取需要start的服務(wù)列表
//2. 然后調(diào)用startServicesIfNeed()繼續(xù)啟動(dòng)
//注意:看到這里,其實(shí)大家應(yīng)該大膽假設(shè),getSystemUIServiceComponents函數(shù)
//是不是通過Dagger2的依賴得到的。如果不是為什么?
String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}
private void startServicesIfNeeded(String metricsPrefix, String[] services) {
//省略判斷
mServices = new SystemUI[services.length];
//啟動(dòng)完成緩存對(duì)象的修改,簡單,略
//首先獲取DumpManager
final DumpManager dumpManager = mSysUIComponent.createDumpManager();
//trace跟蹤點(diǎn),略
//挨個(gè)啟動(dòng)服務(wù)
// 1. 首先查看mComponentHelper是否有緩存,如果有則直接使用
// 2. 如果沒有則反射創(chuàng)建
// 3. 創(chuàng)建完成調(diào)用start()
// 4. 判斷是否系統(tǒng)啟動(dòng)完成,如果完成則調(diào)用onBootCompleted()
// 5. 將啟動(dòng)的服務(wù),加入DumpManager中,以便bugreport觸發(fā)其dump
final 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 long
ti = 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主要功能,就是去調(diào)用各個(gè)服務(wù)的start()和onBootCompleted()功能。完成啟動(dòng)。
現(xiàn)在請思考,為什么這部分內(nèi)容,要放在SystemUIApplication中,就不能直接放在SystemUIService中嗎?
在回答這個(gè)問題之前,我們先來看看getSystemUIServiceComponents()函數(shù)如何得到需要啟動(dòng)的服務(wù)的
getSystemUIServiceComponents函數(shù)獲取欲啟動(dòng)的服務(wù)
源碼如下:
public String[] getSystemUIServiceComponents(Resources resources) {
return resources.getStringArray(R.array.config_systemUIServiceComponents);
}
上面的函數(shù)通過,配置的字符串?dāng)?shù)組獲得,可能大家就會(huì)好奇了——為什么不同Dagger2呢?這么方便的東西怎么還配置個(gè)字符串?dāng)?shù)組呀。
為什么這么配置,我想主要有如下的幾個(gè)原因:
- AOSP代碼的編譯,可以使用override功能,將這部分資源文件,放在自定義的目錄下達(dá)到修改的目的
- 歷史原因,以前就是這么配置的,哈哈
現(xiàn)在我們來思考,啟動(dòng)服務(wù)的功能邏輯,為什么就不能放在SystemUIservice中而要放在SystemUIApplication中。
如果要放在SystemUIApplication中,為什么不通過SystemUIApplication來啟動(dòng),而要SystemUIService來啟動(dòng)。
-
除了SystemUIService啟動(dòng)服務(wù)以外,在多用戶的情況下,也需要啟動(dòng)一些服務(wù),而此時(shí),SystemUI應(yīng)用
先調(diào)用SystemUIApplication,而不會(huì)調(diào)用SystemUIService。因?yàn)镾ystemUIService的觸發(fā)是由system_server啟動(dòng)的。
加上,監(jiān)聽系統(tǒng)的啟動(dòng)邏輯,需要統(tǒng)一處理,將啟動(dòng)邏輯,放入SystemUIApplication,變得理所當(dāng)然。
注意:顯然這就導(dǎo)致一個(gè)bug,倘若SystemUI中途報(bào)錯(cuò),停止運(yùn)行,當(dāng)其再次運(yùn)行的時(shí)候,由SystemUIService啟動(dòng)的各個(gè)
服務(wù),還能夠正確的初始化嗎?顯然不能,這在我寫這邊文章過程中,經(jīng)常出現(xiàn)
-
既然啟動(dòng)邏輯已經(jīng)放入了SystemUIApplication中,那么由SystemUIApplication來啟動(dòng)這部分服務(wù)不可以嗎?
為什么要單獨(dú)一個(gè)SystemUIService作為入口進(jìn)行啟動(dòng)呢?要回答這個(gè)問題,就需要知道,Android的應(yīng)用啟動(dòng),是通過啟動(dòng)某個(gè)待運(yùn)行的組件。即system_server若要運(yùn)行SystemUI,必然要啟動(dòng)某個(gè)組件,而不能只啟動(dòng)Application。
故此,我們需要一個(gè)用來啟動(dòng)的組件,這個(gè)組件就是SystemUI. 由它負(fù)責(zé)各個(gè)具體服務(wù)的啟動(dòng)。加上對(duì)開機(jī)廣播的監(jiān)聽,多用戶下的協(xié)作,就將這部分內(nèi)容,放在了SystemUIApplication中完成。
又因?yàn)镾ystemUIService需要依賴注入,所以創(chuàng)建了SystemUIAppComponentFactory來實(shí)現(xiàn)對(duì)應(yīng)的依賴注入。
至此,我們已經(jīng)完全弄清了,SystemUIService,SystemUIApplication,SystemUIAppComponentFactory的啟動(dòng)流程
以及為什么這么分配功能?,F(xiàn)總結(jié)如下。
- system_server啟動(dòng)之后,開始啟動(dòng)各種服務(wù)
- 在啟動(dòng)其他服務(wù)的時(shí)候,會(huì)先通過PackageManager,獲得要啟動(dòng)systemui的組件名字,然后根據(jù)名字啟動(dòng)systemui組件
- 在上面一步獲得的名字就是SystemUIService。
- 啟動(dòng)SystemUIService,則會(huì)先創(chuàng)建SystemUIApplication,在創(chuàng)建之前會(huì)先調(diào)用SystemUIAppComponentFactory添加相應(yīng)的依賴注入
- SystemUIApplication創(chuàng)建之后,則會(huì)監(jiān)聽系統(tǒng)的啟動(dòng)廣播。
- 接著創(chuàng)建SystemUIService,再創(chuàng)建之前,還會(huì)先調(diào)用SystemUIAppComponentFactory相應(yīng)的方法,添加依賴注入
- 創(chuàng)建SystemUIService之后,通過SystemUIApplication啟動(dòng)各種服務(wù)
至此,整個(gè)SystemUI啟動(dòng)完成。
給SystemUI添加一個(gè)自定義的服務(wù)
有了前面的分析,我們現(xiàn)在需要,進(jìn)行測試一下:寫一個(gè)自定義服務(wù)的模塊,在這個(gè)模塊中,我們僅僅打印出其啟動(dòng)過程即可。
- 新建一個(gè)類,叫做PrintLogService,這個(gè)類繼承SystemUI即可。如下
public class PrintLogService extends SystemUI{
private String TAG = "PrintLogService";
//使用@Inject標(biāo)記,讓Dagger2自動(dòng)管理依賴
@Inject
public PrintLogService(Context context) {
super(context);
}
//簡單打印log
@Override
public void start() {
Slog.d(TAG, "Start PrintLogService");
}
//簡單打印log
@Override
protected void onBootCompleted() {
Slog.d(TAG,"PrintLogService boot completed");
}
}
- 將這個(gè)類的名字放入配置數(shù)組中即config_systemUIServiceComponents如下:
<!-- 最后一行加入我們自定義的服務(wù) -->
<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.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.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.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>
<item>com.android.systemui.wmshell.WMShell</item>
<item>com.android.systemui.PrintLogService</item>
</string-array>
- 使用如下的命令,進(jìn)行編譯,并push到手機(jī)中
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殺死現(xiàn)有SystemUI進(jìn)程,即可
- 從log中我們可以看到如下的輸出
這表示我們自定義的服務(wù)啟動(dòng)成功?。?!
本文完?。?mark hidden color="red">文章來源:http://www.zghlxwxcb.cn/news/detail-736112.html
在文中,我們僅僅對(duì)于mContextAvailableCallback.onContextAvailable(this);一筆帶過
這里面是關(guān)于Dagger2中各種Component的初始化,下一篇文章,將會(huì)從這個(gè)函數(shù)出發(fā),一探SystemUI中各種Component的初始化,理解SystemUI中各個(gè)組件應(yīng)該怎樣被使用。文章來源地址http://www.zghlxwxcb.cn/news/detail-736112.html
到了這里,關(guān)于Android 12 源碼分析 —— 應(yīng)用層 二(SystemUI大體組織和啟動(dòng)過程)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!