天天看點

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化
本文首發微信公衆号:菜天Android

零、前言

上篇『圖文并茂的介紹:D』中我對 Android 8.0 中的SystemUI 作了簡要的介紹,自我感覺很不錯,又是思維導圖又是截圖的,覺得會對不了解的人會有幫助。但可能是太簡要了,被罵是水貨。qaq,呸呸呸!

這篇的話,将對SystemUI的啟動和大體的初始化作描述。篇幅應該比上篇多了些。哈哈。

老樣子,先上目錄,簡潔明了。

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化

一、概述

由于需要實時回報系統狀态,如藍牙開關、wifi開關、時間及相應使用者導航欄操作,SystemUI從系統一啟動就被帶起來了(SystemUI:我也不想啊!老累了!)。正常使用過程中的SystemUI,大多數功能子產品都是出于運作狀态,隻有少數功能,比如:截屏功能,你長按電源+音量下鍵才會咔嚓截屏。

這裡簡要說下系統啟動過程:由init程序->Zygote程序->SystemServer程序。

那init程序又是怎麼起來的?當你按下電源鍵,系統上電,從固定位址開始加載固化在ROM的Bootloader代碼到RAM中并執行,Bootloader引導程式負責将系統OS拉起。當系統OS被拉起,并完成一些列初始化和系統設定後,就會首先在系統檔案中尋找“init”檔案并啟動這個咱們使用者空間的第一個程序。

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化

Emmm,扯遠了,回到主題。按照一開始的系統啟動過程,我們的SystemUI程序是在SystemServer的啟動過程中被帶起來。

從第一篇介紹我們知道,SystemUI有着很多的子產品且對應着相應的界面。這些子產品有些共同的地方,例如都需要:

  • 處理各自子產品的初始化
  • 處理系統的狀态變化
  • 執行dump
  • 系統啟動完成時,要處理相應邏輯

是以在代碼中展現為:将這些共同點提取并抽象,形成SystemUI抽象類,結構如下。

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化

簡單對這段代碼說明下:

  1. 為子類定義了一個start方法供子類完成初始化,這個方法是一個抽象方法,是以具體的子類必現實作。
  2. onConfigurationChanged是處理系統狀态變化的回調,這裡的狀态變化包括:時區變更,字型大小變更,輸入模式變更,螢幕大小變更,螢幕方向變更等。
  3. 系統中很多的子產品都包含了dump方法。dump方法用來将子產品的内部狀态dump到輸出流中,這個方法主要是輔助調試所用。開發者可以在開發過程中,通過adb shell執行dump來了解系統的内部狀态。
  4. onBootCompleted是系統啟動完成的回調方法。

除了截屏服務,提及子產品均繼承抽象類SystemUI并在應用啟動時被分别初始化。從這種角度來看,SystemUI應用更像是這些功能子產品的容器。

值得一提的是,和Nougat相比,在Oreo中,抽象類SystemUI的兩個子類:BaseStatusBar和PhoneStatusBar被合并替換成了StatusBar.java。相信了解接觸過SystemUI子產品的老司機對PhoneStatusBar這個核心類一定很熟悉和,現在也塵歸塵、土歸土咯。

二、SystemUI啟動流程

SystemServer負責系統中各種重要服務的啟動,不巧,由于SystemUI的重要性,她也在被啟動之列,雖然是處于“Other”的地位~(SystemServer的代碼對系統服務類别大體分為三類:Bootstrap->Core->Other,SystemUI的啟動就在Other中)。

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化

在startOtherServices()中,通過調用AMS的systemReady()方法通知AMS準備就緒。systemReady()擁有一個名為goingCallback的Runnable執行個體作為參數,So,當AMS完成對systemReady()的處理後将會回調這一Runnable的run()方法。

private void startOtherServices() {
    ... //省略大概1000行
    mActivityManagerService.systemReady(() -> {
        Slog.i(TAG, "Making services ready");
        ...
        traceBeginAndSlog("StartSystemUI");
        try {
            startSystemUi(context, windowManagerF);
        } catch (Throwable e) {
            reportWtf("starting System UI", e);
        }
        ...
    }       
}
           

并在startSystemUi方法中,通過紅框中的元件名啟動了SystemUI中的SystemUIService服務

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化

對于Android系統來說,當一個應用啟動,系統會保證其Application類是第一個被執行個體化的類,并且Application的onCreate方法,一定先于應用中所有的Activity,Service和BroadcastReceiver的建立。

SystemUI中,SystemUIApplication就是第一個被執行個體化的類。

在其中,定義了兩組服務:

  • 一類是所有使用者共用的SystemUI服務,例如:Status Bar
  • 一類是每個使用者獨有的服務

下面的兩個數組記錄了這兩組服務

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化

前面也提到,SystemUI抽取了功能子產品的共性形成抽象類SystemUI.class,上圖中所有列出的類型,均是SystemUI的子類實作。

接着說,在SystemUIApplication中,onCreate方法被調用:主要注冊一個廣播接收器,用以接收BOOT_COMPLETED廣播,在接收到廣播後,調用各子產品的函數onBootCompleted。

你還記的SystemServer中啟動SystemUI的代碼不?那個目标是SystemUIService的Intent。

當SystemApplication家的onCreate執行完畢,就會去啟動這個SystemUIService。按照規矩,此服務onCreate方法在啟動時被調用。

不研究不知道,哇靠,這家夥隻是個中轉代理(給别人一個啟動你的機會)而已~,不信你看,實際代碼隻有下面一行,看下圖。

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化

整個服務,真正幹活的隻有紅框中的一句話。仔細瞅瞅,調用的是SystemUIApplication中的startServicesIfNeeded方法,轉了一圈又回來了。

這就是啟動各子產品的地方。

private void startServicesIfNeeded(Class<?>[] services) {
    if (mServicesStarted) {
        return;
    }

    if (!mBootCompleted) {
        // check to see if maybe it was already completed long before we began
        // see ActivityManagerService.finishBooting()
        if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
            // sys.boot_completed屬性值,在系統boot完成後,AMS會對其進行設定
            mBootCompleted = true;
            if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
        }
    }

    Log.v(TAG, "Starting SystemUI services for user " + 
            Process.myUserHandle().getIdentifier() + ".");
    final int N = services.length;
    for (int i = 0; i < N; i++) {
        Class<?> cl = services[i];
        if (DEBUG) Log.d(TAG, "loading: " + cl);
        try {
            // SystemUIFactory類在6.0上還沒有,是7.0上出現的,目的是為了提供可定制化的SystemUI
            Object newService = SystemUIFactory.getInstance().createInstance(cl);
            mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex);
        } catch (InstantiationException ex) {
            throw new RuntimeException(ex);
        }

        // 對數組中的每一個Service都進行初始化
        mServices[i].mContext = this;
        mServices[i].mComponents = mComponents;
        if (DEBUG) Log.d(TAG, "running: " + mServices[i]); 
        mServices[i].start();

        if (mBootCompleted) {
            mServices[i].onBootCompleted();
        }
    }
    ...
}
           

對于SystemUI App啟動,這裡總結了一張啟動時序圖給大家參考,如下所示:

Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化

進一步的話,就到了各個子產品獨自的初始化邏輯了。

這裡我們單獨對SystemBars的初始進行進一步的說明。

三、舉例說明:SystemBars

SystemBars主要包含了NavigationBar和StatusBar,不知道這兩個Bar對應位置的同學可以看下第一篇『圖文并茂的介紹:D』。

Show the code:

public class SystemBars extends SystemUI {
    private static final String TAG = "SystemBars";    
    private static final boolean DEBUG = false;    
    private static final int WAIT_FOR_BARS_TO_DIE = 500; 
       
    // in-process fallback implementation, per the product config
    private SystemUI mStatusBar;    
    
    @Override
    public void start() {    
        if (DEBUG) Log.d(TAG, "start");        
        createStatusBarFromConfig();
    }    
     
    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {        
        if (mStatusBar != null) {            
            mStatusBar.dump(fd, pw, args);
        }
    }    
    
    private void createStatusBarFromConfig() {        
        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
        final String clsName = mContext.getString(R.string.config_statusBarComponent);        
        if (clsName == null || clsName.length() == 0) {
            throw andLog("No status bar component configured", null);
        }        
        Class<?> cls = null;        
        try {         
            cls = mContext.getClassLoader().loadClass(clsName);
        } catch (Throwable t) {    
            throw andLog("Error loading status bar component: " + clsName, t);
        }   
        try {       
            mStatusBar = (SystemUI) cls.newInstance();
        } catch (Throwable t) { 
            throw andLog("Error creating status bar component: " + clsName, t);
        }        
        mStatusBar.mContext = mContext; 
        mStatusBar.mComponents = mComponents; 
        mStatusBar.start(); 
        if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
    } 
 
    private RuntimeException andLog(String msg, Throwable t) {
        Log.w(TAG, msg, t);
        throw new RuntimeException(msg, t);
    }
}
           

這段代碼說明如下:

  1. start方法由SystemUIApplication調用
  2. 調用createStatusBarFromConfig方法,根據配置檔案中的資訊來進行Status Bar的初始化
  3. 讀取配置檔案中實作類的類名。這個值于frameworks/base/packages/SystemUI/res/values/config.xml中定義。在手機中,其值是:com.android.systemui.statusbar.phone.StatusBar
  4. 通過類加載器加載對應的類
  5. 通過反射API建立對象執行個體
  6. 最後調用執行個體的start方法對其進行初始化。如果是手機裝置,這裡就是StatusBar.start方法

為什麼要讀取資源檔案擷取類名并通過反射來建立執行個體呢?

好處是:

這裡将類名配置在資源檔案中,那麼對于Tv和Car這些不同的平台,可以不用修改任何的代碼,隻需要修改配置檔案,便替換了系統中狀态欄的實作,由此減少了子產品間的耦合,也減少了系統的維護成本。

值得我們借鑒。

好了,SystemUI的啟動到這裡就結束了,具體SystemBars以及StatusBar都做了些什麼,後續會進行跟進。

歡迎關注微信公衆号 菜天Android

主推 Android 幹貨文章 關注得 Android 詳細知識圖譜
Android 8.0 SystemUI 源碼分析(二):啟動流程和初始化