国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Android 12 源碼分析 —— 應(yīng)用層 二(SystemUI大體組織和啟動(dòng)過程)

這篇具有很好參考價(jià)值的文章主要介紹了Android 12 源碼分析 —— 應(yīng)用層 二(SystemUI大體組織和啟動(dòng)過程)。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

Android 12 源碼分析 —— 應(yīng)用層 二(SystemUI大體組織和啟動(dòng)過程)

在前一篇文章中,我們介紹了SystemUI怎么使用IDE進(jìn)行編輯和調(diào)試。這是分析SystemUI的最基礎(chǔ),希望讀者能盡量掌握。

本篇文章,將會(huì)介紹SystemUI的大概組織架構(gòu),以及它的啟動(dòng)過程。本篇文章讀完,將會(huì)知道:

  1. SystemUI為什么選擇使用Dagger2
  2. SystemUI怎么新建一個(gè)模塊
  3. SystemUI的啟動(dòng)流程

在進(jìn)行閱讀之前,請跟著我思考如下的問題:

  1. SystemUI要完成哪些功能?
  2. 各個(gè)功能之間需要溝通嗎?
  3. 倘若各個(gè)功能之間需要進(jìn)行溝通,怎樣組織他們之間的引用關(guān)系
  4. 各個(gè)功能需要與系統(tǒng)服務(wù)溝通嗎?
  5. 倘若各個(gè)功能需要與系統(tǒng)服務(wù)溝通,怎么高效的組織他們之間的引用
  6. 各個(gè)功能之間有依賴關(guān)系嗎?
  7. 各個(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篇

  1. LongScreenShotActivity:長截圖使用的視圖,用戶決定長截圖時(shí),調(diào)起這個(gè)Activity。
  2. ScreenRecordDialog:錄屏?xí)r彈出的選項(xiàng)框視圖。
  3. TunerActivity:這是給研發(fā)人員用的微調(diào)界面??梢允褂孟旅婷畲蜷_界面入口
adb shell pm enable com.android.systemui/com.android.systemui.tuner.TunerActivity

然后在設(shè)置->系統(tǒng)->System UI Tuner進(jìn)入界面

  1. DemoMode:SystemUI的Demo模式,也是給研發(fā)人員用的,他是TunerActivity的功能補(bǔ)充,可在開發(fā)者選項(xiàng)中打開
  2. ForceReSizableInfoActivity:彈出應(yīng)用無法在分屏模式,或者輔助屏幕下運(yùn)行
  3. UsbPermissionActivity:確定USB權(quán)限彈框
  4. UsbResolverActivity:為USB設(shè)備選擇一個(gè)應(yīng)用彈框
  5. UsbConfirmActivity:彈出一個(gè)視圖,用來確定是否要使用某個(gè)app,是UsbResolverActivity的后續(xù)視圖
  6. SensorUseStartedActivity:當(dāng)傳感器在privacy mode下,欲使用傳感器時(shí)的彈框
  7. TvUnblockSensorActivity:同SensorUseStartedActivity,只不過這個(gè)是運(yùn)用在電視機(jī)上的視圖
  8. UsbAccessoryUriActivity:彈出一個(gè)框,讓你去下載這個(gè)USB設(shè)備對(duì)應(yīng)的應(yīng)用
  9. UsbContaminantActivity:彈出一個(gè)框,表示USB已經(jīng)停用,停用的原因可能是usb端口處有贓物等。
  10. UsbDebuggingActivity:彈出是否允許USB調(diào)試
  11. UsbDebuggingActivityAlias:這個(gè)是UsbDebuggingActivity的別名
  12. WifiDebuggingActivity:彈出是否允許網(wǎng)絡(luò)進(jìn)行無線調(diào)試
  13. WifiDebuggingActivityAlias:是WifiDebuggingActivity的別名
  14. WifiDebuggingSecondaryUserActivity:彈出目前登錄的用戶無法開啟無線調(diào)試功能,需要切換為主用戶
  15. NetworkOverLimitActivity:彈出數(shù)據(jù)流量已經(jīng)達(dá)到上限
  16. MediaProjectionPermissionActivity:多媒體投屏權(quán)限確認(rèn)
  17. TvNotificationPanelActivity:TV專用,彈出一個(gè)消息框
  18. SlicePermissionActivity:Slice權(quán)限彈框
  19. DessertCase:彩蛋之一
  20. MLandActivity:彩蛋小游戲
  21. PeopleSpaceActivity:提示Pepole Space UI的位置,android 11新增功能
  22. LaunchConversationActivity:當(dāng)會(huì)話被點(diǎn)擊的時(shí)候,展開視圖,Android 11 新增功能
  23. WorkLockActivity:解鎖work profile的界面
  24. CreateUserActivity:創(chuàng)建用戶視圖
  25. Somnambulator:屏保
  26. BrightnessDialog:亮度彈框
  27. ForegroundServicesDialog:展示前臺(tái)services的一個(gè)彈框
  28. ChooserActivity:彈出一個(gè)框,讓用戶選擇打開哪一個(gè)應(yīng)用,來處理當(dāng)前的Intent
  29. ControlsProviderSelectorActivity 彈出“選擇要添加控制器的應(yīng)用”
  30. ControlsEditingActivity:編輯控制器,拖拽進(jìn)行編輯
  31. ControlsFavoritingActivity:控制器,偏好設(shè)置
  32. ControlsActivity:列出設(shè)備控制器
  33. WalletActivity:電子錢包
  34. ControlsRequestDialog:control請求添加設(shè)備控制器彈框

注意:這里的Controls,是外部設(shè)備的控制器,如全屋智能中的控制器。

上面只是一個(gè)非常簡單的概覽,而一些常見組件邏輯和UI細(xì)節(jié),將會(huì)在后續(xù)文章中出現(xiàn)。

看到這里,可能會(huì)有讀者提問:上面的Activity似乎沒有狀態(tài)欄和鎖屏呀,他們的視圖,難道不在這些Activity里面嗎?

欲探討這個(gè)問題,還需要先看剩下的組件。

Services篇

  1. SystemUIService:哇哦,多么讓人提神的名字,這個(gè)Service包含了SystemUI內(nèi)部的大部分功能它也是我們SystemUI源碼分析的重中之重。
  2. SystemUISecondaryUserService:多用戶情況下,該service保證多用戶的SystemUI功能正常
  3. SystemUIAuxiliaryDumpService:開發(fā)使用,dump出各個(gè)必要部件的信息并查看
  4. TakeScreenshotService:截屏相關(guān)的service
  5. RecordingService:錄屏相關(guān)的service
  6. ImageWallpaper:壁紙相關(guān)的service
  7. PeopleBackupFollowUpJob:People service ui相關(guān)的服務(wù)
  8. DessertCaseDream:小彩蛋
  9. KeyguardService:鎖屏相關(guān)的服務(wù)
  10. AuxiliaryPersistenceWrapper$DeletionJobService:外部設(shè)備控制器相關(guān)的服務(wù)
  11. DozeService:跟Doze相關(guān)的服務(wù)

ContentProvider篇

  1. FileProvider:提供文件
  2. KeyguardSliceProvider:提供鎖屏Slice
  3. ClockOptionsProvider:為選擇器程序提供時(shí)鐘預(yù)覽
  4. PeopleProvider:返回給定快捷方式的 People Tile 預(yù)覽

BroadcastReceiver篇

  1. ScreenshotServiceErrorReceiver:截屏失敗廣播接收器
  2. SysuiRestartReceiver:重啟SystemUI廣播接收器
  3. ActionProxyReceiver:攔截share和edit intent,便于提前處理一些事情的廣播接收器
  4. DeleteScreenshotReceiver:刪除截屏廣播接收器
  5. SmartActionsReceiver:用戶點(diǎn)擊通知中的smart action之后,用于接收對(duì)應(yīng)的廣播,并執(zhí)行smart action
  6. ControlsRequestReciver:接收增加控制器請求的廣播接收器
  7. TunerService$ClearReciver:用于調(diào)用TunerService的clear的廣播接收器
  8. KeyboardShortcutsReceiver:展示或者隱藏鍵盤快捷鍵的廣播接收器
  9. MediaOutputDialogReceiver:接收媒體輸出Intent的廣播接收器
  10. PeopleSpaceWidgetPinnedReceiver:當(dāng)一個(gè)聯(lián)系人Tile widget被添加之后,這個(gè)接收器被調(diào)用
  11. 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的簡略說明:

  1. 系統(tǒng)啟動(dòng)system_server進(jìn)程之后,會(huì)執(zhí)行
new SystemServer().run();
  1. 在run()方法中,會(huì)去啟動(dòng)各種服務(wù),這些服務(wù)包括:
  • 啟動(dòng)引導(dǎo)服務(wù)
  • 核心服務(wù)
  • 其他服務(wù)
  1. 在啟動(dòng)其他服務(wù)的時(shí)候,會(huì)去啟動(dòng)SystemUI(通過Intent)。而要獲得啟動(dòng)SystemUI的具體組件,就通過
    Android的PackageManager得到,

  2. 而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)過程了

  1. Android系統(tǒng)啟動(dòng)完成,啟動(dòng)system_server
  2. system_server,根據(jù)配置,通過Intent來啟動(dòng)SystemUI的組件
  3. SystemUI在啟動(dòng)組件之前,會(huì)先創(chuàng)建SystemUIAppComponentFactory對(duì)象,然后調(diào)用其相應(yīng)方法
  4. 接著,SystemUI會(huì)創(chuàng)建SystemUIApplication,然后調(diào)用其相應(yīng)方法
  5. 最后,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):

  1. 相應(yīng)組件的創(chuàng)建,如SystemUIApplication,SystemUIService等
  2. mComponentHelper的初始化,也即通過注入實(shí)現(xiàn)
  3. 而在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è)原因:

  1. AOSP代碼的編譯,可以使用override功能,將這部分資源文件,放在自定義的目錄下達(dá)到修改的目的
  2. 歷史原因,以前就是這么配置的,哈哈

現(xiàn)在我們來思考,啟動(dòng)服務(wù)的功能邏輯,為什么就不能放在SystemUIservice中而要放在SystemUIApplication中。
如果要放在SystemUIApplication中,為什么不通過SystemUIApplication來啟動(dòng),而要SystemUIService來啟動(dòng)。

  1. 除了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)

  1. 既然啟動(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é)如下。

  1. system_server啟動(dòng)之后,開始啟動(dòng)各種服務(wù)
  2. 在啟動(dòng)其他服務(wù)的時(shí)候,會(huì)先通過PackageManager,獲得要啟動(dòng)systemui的組件名字,然后根據(jù)名字啟動(dòng)systemui組件
  3. 在上面一步獲得的名字就是SystemUIService。
  4. 啟動(dòng)SystemUIService,則會(huì)先創(chuàng)建SystemUIApplication,在創(chuàng)建之前會(huì)先調(diào)用SystemUIAppComponentFactory添加相應(yīng)的依賴注入
  5. SystemUIApplication創(chuàng)建之后,則會(huì)監(jiān)聽系統(tǒng)的啟動(dòng)廣播。
  6. 接著創(chuàng)建SystemUIService,再創(chuàng)建之前,還會(huì)先調(diào)用SystemUIAppComponentFactory相應(yīng)的方法,添加依賴注入
  7. 創(chuàng)建SystemUIService之后,通過SystemUIApplication啟動(dòng)各種服務(wù)

至此,整個(gè)SystemUI啟動(dòng)完成。

給SystemUI添加一個(gè)自定義的服務(wù)

有了前面的分析,我們現(xiàn)在需要,進(jìn)行測試一下:寫一個(gè)自定義服務(wù)的模塊,在這個(gè)模塊中,我們僅僅打印出其啟動(dòng)過程即可。

  1. 新建一個(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");
    }

}
  1. 將這個(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>

  1. 使用如下的命令,進(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)程,即可

  1. 從log中我們可以看到如下的輸出

systemui tuner,Android12 SystemUI,SystemUI dagger,SystemUI啟動(dòng)流程,SystemUI架構(gòu),SystUI設(shè)計(jì)邏輯,SysUI各個(gè)組件

這表示我們自定義的服務(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)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Android 應(yīng)用層 到 HAL 層

    Android 應(yīng)用層 到 HAL 層

    如下 AOSP軟件堆棧架構(gòu)圖 主要跨兩個(gè)階段 Android Apps == Android Framework == HAL ,這種 IPC跨進(jìn)程通信 在 Android 中必須要了解的相關(guān)知識(shí)點(diǎn): 1》 Binder IPC 通信機(jī)制;2》 JNI 調(diào)用;3》 AIDL、HIDL 接口定義語言 1》 Binder IPC 通信機(jī)制 IPC 域 說明 /dev/binder 框架/應(yīng)用進(jìn)程之間的 IPC,使用

    2024年02月10日
    瀏覽(18)
  • 4.1 應(yīng)用層Hook掛鉤原理分析

    4.1 應(yīng)用層Hook掛鉤原理分析

    InlineHook 是一種計(jì)算機(jī)安全編程技術(shù),其原理是在計(jì)算機(jī)程序執(zhí)行期間進(jìn)行攔截、修改、增強(qiáng)現(xiàn)有函數(shù)功能。它使用鉤子函數(shù)(也可以稱為回調(diào)函數(shù))來截獲程序執(zhí)行的各種事件,并在事件發(fā)生前或后進(jìn)行自定義處理,從而控制或增強(qiáng)程序行為。Hook技術(shù)常被用于系統(tǒng)加速、功

    2024年02月08日
    瀏覽(17)
  • 《計(jì)算機(jī)網(wǎng)絡(luò)》實(shí)驗(yàn)八 應(yīng)用層相關(guān)協(xié)議分析

    《計(jì)算機(jī)網(wǎng)絡(luò)》實(shí)驗(yàn)八 應(yīng)用層相關(guān)協(xié)議分析

    實(shí)驗(yàn)八 應(yīng)用層相關(guān)協(xié)議分析.............................................................................................. 2 一、實(shí)驗(yàn)?zāi)康?................................................................................................................... 2 二、實(shí)驗(yàn)原理.......................................................................

    2024年02月03日
    瀏覽(20)
  • 根據(jù)源碼,模擬實(shí)現(xiàn) RabbitMQ - 網(wǎng)絡(luò)通訊設(shè)計(jì),自定義應(yīng)用層協(xié)議,實(shí)現(xiàn) BrokerServer (8)

    根據(jù)源碼,模擬實(shí)現(xiàn) RabbitMQ - 網(wǎng)絡(luò)通訊設(shè)計(jì),自定義應(yīng)用層協(xié)議,實(shí)現(xiàn) BrokerServer (8)

    目錄 一、網(wǎng)絡(luò)通訊協(xié)議設(shè)計(jì) 1.1、交互模型 1.2、自定義應(yīng)用層協(xié)議 1.2.1、請求和響應(yīng)格式約定 ?編輯 1.2.2、參數(shù)說明 1.2.3、具體例子 1.2.4、特殊栗子 1.3、實(shí)現(xiàn) BrokerServer 1.3.1、屬性和構(gòu)造 1.3.2、啟動(dòng) BrokerServer 1.3.3、停止 BrokerServer 1.3.4、處理每一個(gè)客戶端連接 1.3.5、讀取請求

    2024年02月10日
    瀏覽(20)
  • 【音視頻|ALSA】基于alsa-lib開發(fā)ALSA應(yīng)用層程序--附帶源碼

    【音視頻|ALSA】基于alsa-lib開發(fā)ALSA應(yīng)用層程序--附帶源碼

    ??博客主頁??:??https://blog.csdn.net/wkd_007?? ??博客內(nèi)容??:??嵌入式開發(fā)、Linux、C語言、C++、數(shù)據(jù)結(jié)構(gòu)、音視頻?? ??本文內(nèi)容??:??基于alsa-lib開發(fā)ALSA應(yīng)用層程序?? ??金句分享??:??蓋士人讀書,第一要有志,第二要有識(shí),第三要有恒。有志則斷不甘為下流,有

    2024年02月08日
    瀏覽(32)
  • Android 12系統(tǒng)源碼_SystemUI(八)SystemUIVisibility屬性

    在Android系統(tǒng)中,很多應(yīng)用都需要根據(jù)具體情況來控制狀態(tài)欄和導(dǎo)航欄的顯示和隱藏,又或者將狀態(tài)欄透明,實(shí)現(xiàn)諸如沉浸式、全面屏燈效果,而要實(shí)現(xiàn)這些效果,都離不開SystemUIVisibility屬性。由于SystemUIVisibilityy屬性主要用來控制系統(tǒng)狀態(tài)欄和導(dǎo)航欄的行為,而狀態(tài)欄和導(dǎo)航

    2024年02月06日
    瀏覽(20)
  • 【計(jì)算機(jī)網(wǎng)絡(luò)概述】第二章:應(yīng)用層:2.1 應(yīng)用層原理

    【計(jì)算機(jī)網(wǎng)絡(luò)概述】第二章:應(yīng)用層:2.1 應(yīng)用層原理

    客戶端/服務(wù)器模式 peer to peer 模式(對(duì)等模式) ???????在第一章的內(nèi)容中,我們學(xué)習(xí)了計(jì)算機(jī)網(wǎng)絡(luò)的大體輪廓,因?yàn)樵谟?jì)算機(jī)網(wǎng)絡(luò)中,我們需要記住非常多的專有名詞,所以在第一章中,我們需要進(jìn)行非常多的記憶。第一章還是非常重要的。 ???????比如,我們所使用

    2024年01月18日
    瀏覽(57)
  • 應(yīng)用層與傳輸層~

    應(yīng)用層與傳輸層~

    應(yīng)用層是負(fù)責(zé)應(yīng)用程序之間溝通的一層。由于不同的網(wǎng)絡(luò)應(yīng)用的應(yīng)用進(jìn)程之間,有著不同的通信規(guī)則,因此自然就需要應(yīng)用層協(xié)議來解決這些問題,這就構(gòu)成了應(yīng)用層的主要內(nèi)容即:精確定義這些通信規(guī)則。 應(yīng)用層有不少應(yīng)用廣泛的協(xié)議,像域名系統(tǒng)(DNS)、文件傳輸協(xié)議(

    2023年04月08日
    瀏覽(20)
  • 應(yīng)用層協(xié)議 HTTP

    應(yīng)用層協(xié)議 HTTP

    我們已經(jīng)學(xué)過 TCP/IP , 已然知道數(shù)據(jù)能從客戶端進(jìn)程經(jīng)過路徑選擇跨網(wǎng)絡(luò)傳送到服務(wù)器端進(jìn)程。 我們還需要知道的是,我們把數(shù)據(jù)從 A 端傳送到 B 端, TCP/IP 解決的是順豐的功能,而兩端還要對(duì)數(shù)據(jù)進(jìn)行加工處理或者使用,所以我們還需要一層協(xié)議,不關(guān)心通信細(xì)節(jié),關(guān)心應(yīng)用

    2024年02月06日
    瀏覽(24)
  • 【網(wǎng)絡(luò)】-- 應(yīng)用層

    【網(wǎng)絡(luò)】-- 應(yīng)用層

    目錄 TCP協(xié)議通訊流程 三次握手的過程 四次揮手的過程 TCP 和 UDP 對(duì)比 應(yīng)用層 \\\"協(xié)議\\\" 網(wǎng)絡(luò)版計(jì)算器 認(rèn)識(shí)TCP 守護(hù)進(jìn)程 鋪墊講解 守護(hù)進(jìn)程講解 setsid 總結(jié) ????????下圖是基于TCP 協(xié)議的客戶端 / 服務(wù)器程序的一般流程: TCP是面向連接的通訊協(xié)議,在通訊之前,需要進(jìn)行 3次握

    2023年04月09日
    瀏覽(60)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包