SystemUI啓動流程及主體佈局介紹

http://www.jianshu.com/p/0ab1279465fa

本文將基於Android 6.0代碼,分析systemUI的啓動加載流程,對systemUI幾處關鍵的視圖的佈局及功能進行介紹。

一. SystemUI主體框架啓動流程

android設備上電,引導程序引導進入boot(通常是uboot),加載initramfs、kernel鏡像,啓動kernel後,進入用戶態程序。第一個用戶空間程序是init, PID固定是1.
init的基本功能有:

  • 管理設備
  • 解析並處理Android啓動腳本init.rc
  • 實時維護這個init.rc中的服務,包括加載 Zygote

而在Zygote中將啓動SystemServer組件。

本文將從SystemServer開始分析。
SystemServer 名爲系統服務進程,負責啓動 Android 系統的關鍵服務。
其入口是SystemServer.main():

/**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }

可以看到main()中生成了SystemServer對象並執行了run方法。
SystemServer.run():

private void run() {
    ......

     // Start services.
        try {
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
            ......
       } catch (Throwable ex) {
           ...
            throw ex;
        }
        ...
        // Loop forever.
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
}

先看一眼startBootstrapServices();

 private void startBootstrapServices() {
        ......
        Installer installer = mSystemServiceManager.startService(Installer.class);

        // Activity manager runs the show.
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        ......
 }

在startBootstrapServices()中啓動了mActivityManagerService。
隨後,我們再回頭去看startOtherServices():

private void startOtherServices() {
        final Context context = mSystemContext;
        AccountManagerService accountManager = null;
        ContentService contentService = null;
        .......

         mActivityManagerService.systemReady(new Runnable() {
            @Override
            public void run() {
              ......

                try {
                    startSystemUi(context);
                } catch (Throwable e) {
                    reportWtf("starting System UI", e);
                }
         .......

mActivityManagerService.systemReady創建線程去執行startSystemUi(context),從方法名稱可以看出,這裏將啓動systemUI。

static final void startSystemUi(Context context) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.android.systemui",
                    "com.android.systemui.SystemUIService"));
        //Slog.d(TAG, "Starting service: " + intent);
        context.startServiceAsUser(intent, UserHandle.OWNER);
    }

通過intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
設置啓動systemui程序的SystemUIService
進入SystemUIService:

public class SystemUIService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
    }
......

onCreate方法中獲得SystemUIApplication對象並調用其startServicesIfNeeded方法:

 public void startServicesIfNeeded() {
        final int N = SERVICES.length;
        for (int i=0; i<N; i++) {
            Class<?> cl = SERVICES[i];
            try {
                mServices[i] = (SystemUI)cl.newInstance();//加載實例
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InstantiationException ex) {
                throw new RuntimeException(ex);
            }
            mServices[i].mContext = this;
            mServices[i].mComponents = mComponents;
            mServices[i].start();//start服務
            if (mBootCompleted) {
                mServices[i].onBootCompleted();
            }
        }
        mServicesStarted = true;
    }

可以看到startServicesIfNeeded()循環start了很多Services。
數組SERVICES的定義如下:

private final Class<?>[] SERVICES = new Class[] {
            com.android.systemui.tuner.TunerService.class, //定製狀態欄服務
            com.android.systemui.keyguard.KeyguardViewMediator.class,//鎖屏模塊
            com.android.systemui.recents.Recents.class,//最近應用
            com.android.systemui.volume.VolumeUI.class,//全局音量控制
            com.android.systemui.statusbar.SystemBars.class,//系統狀態欄
            com.android.systemui.usb.StorageNotification.class,//Storage存儲通知
            com.android.systemui.power.PowerUI.class,//電量管理相關
            com.android.systemui.media.RingtonePlayer.class,//鈴聲播放
            com.android.systemui.keyboard.KeyboardUI.class,//鍵盤相關
};

可以看到子服務包括:TunerService,KeyguardViewMediator,Recents,VolumeUI,SystemBars,StorageNotification,PowerUI
RingtonePlayer,KeyboardUI。
不過這裏的service與我們平時所講的四大組件的service並不一樣。這裏的service只是繼承了SystemUI類的普通對象而已。
例如SystemBars:

public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {
           ......
}

小結:

查看到這裏,可以總結一下systemUI主體框架的啓動流程:
SystemServer啓動Android核心服務包括了ActivityManagerService
--->ActivityManagerService一旦啓動完成就會在systemReady的回調裏啓動SystemUIService
--->SystemUIService.onCreate—--->SystemUIApplication.startServicesIfNeeded
--->循環中調用mServices[i].start()啓動SystemUI的各種核心service。

二. SystemUI關鍵視圖呈現

手機中的下拉狀態欄,鎖屏,通知以及最近打開任務列表等功能都是SystemUI實現的。
主要功能點對應的界面如下圖所示:



在上文中我們看到了SystemUI各項服務的啓動流程,那麼服務有了,界面視圖又是如何呈現的呢?
接下來將以SystemBars爲例,它是SystemUI的主要視圖。
上文中mServices[i].start()將調用SystemBars.start():

    @Override
    public void start() {
        if (DEBUG) Log.d(TAG, "start");
        mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
                mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
        mServiceMonitor.start();  // will call onNoService if no remote service is found
    }

start中創建ServiceMonitor實例並start();
註釋中說明,/服務沒啓動時,ServiceMonitor會回調SystemBars的onNoService/
所以去看SystemBars的onNoService:

    @Override
    public void onNoService() {
        if (DEBUG) Log.d(TAG, "onNoService");
        createStatusBarFromConfig();  // fallback to using an in-process implementation
    }

    private void 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 = (BaseStatusBar) cls.newInstance();
        } catch (Throwable t) {
            throw andLog("Error creating status bar component: " + clsName, t);
        }
        mStatusBar.mContext = mContext;
        mStatusBar.mComponents = mComponents;
        mStatusBar.start();
    }

需要注意的是,clsName得到的string爲com.android.systemui.statusbar.phone.PhoneStatusBar
執行SystemBars.start()後,通過反射機制,最終得到BaseStatusBar對象。
這裏需要說明的是BaseStatusBar是PhoneStatusBar的父類。
上文mStatusBar.start()即爲PhoneStatusBar.start():

    public void start() {
            ......
            super.start(); // calls createAndAddWindows()
            ......
    }

再去BaseStatusBar.start():

public void start() {
        mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
        mDisplay = mWindowManager.getDefaultDisplay();
        mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
                Context.DEVICE_POLICY_SERVICE);

        mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);

        mNotificationData = new NotificationData(this);

        mAccessibilityManager = (AccessibilityManager)
                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);

        mDreamManager = IDreamManager.Stub.asInterface(
                ServiceManager.checkService(DreamService.DREAM_SERVICE));
        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);

        .......
        //在這裏實例化了許多systemui常用的對象,服務,Manager,Observer等等
        .......

        createAndAddWindows(); //創建並添加視圖

查看createAndAddWindows():

 protected abstract void createAndAddWindows();

是個抽象方法,很顯然調用去了BaseStatusBar的子類,
即PhoneStatusBar的createAndAddWindows():

 @Override
 public void createAndAddWindows() {
        addStatusBarWindow();
 }

 private void addStatusBarWindow() {
        makeStatusBarView();//關鍵方法,創建StatusBarView
        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
 }

重點來了,makeStatusBarView,創建StatusBarView,
隨後,mStatusBarWindowManager將其添加,呈現給用戶
makeStatusBarView()的代碼很長,但主要代碼是這一句:

protected PhoneStatusBarView makeStatusBarView()
{
    ......
    mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
                R.layout.super_status_bar, null);
    ......
}

通過認識super_status_bar.xml就能認識SystemBars的大體構成。
下面是部分省略的super_status_bar文件

<!-- This is the combined status bar / notification panel window. -->
<com.android.systemui.statusbar.phone.StatusBarWindowView
    android:fitsSystemWindows="true">

    <com.android.systemui.statusbar.BackDropView
            android:id="@+id/backdrop"
            sysui:ignoreRightInset="true">
    </com.android.systemui.statusbar.BackDropView>

    <com.android.systemui.statusbar.ScrimView 
        android:id="@+id/scrim_behind"
        android:importantForAccessibility="no"/>

    <com.android.systemui.statusbar.AlphaOptimizedView
        android:id="@+id/heads_up_scrim"
        android:importantForAccessibility="no"/>

    <include layout="@layout/status_bar"
        android:layout_width="match_parent"

    <FrameLayout android:id="@+id/brightness_mirror">
        <FrameLayout
                android:background="@drawable/brightness_mirror_background">
            <include layout="@layout/quick_settings_brightness_dialog"
                     android:layout_height="wrap_content" />
        </FrameLayout>
    </FrameLayout>

    <com.android.systemui.statusbar.phone.PanelHolder
        android:id="@+id/panel_holder"
        <include layout="@layout/status_bar_expanded"
            android:visibility="gone" />
    </com.android.systemui.statusbar.phone.PanelHolder>

    <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"
        android:importantForAccessibility="no"
        sysui:ignoreRightInset="true"
        />

</com.android.systemui.statusbar.phone.StatusBarWindowView>

通過上述view佈局及makeStatusBarView相關代碼,可以發現mStatusBarWindow(StatusBarWindowView)中包含以下4個部分:

  • ScrimView
  • PhoneStatusBarView layout-> status_bar
  • PanelHolder id->PanelHolder
  • ScrimView

其實這裏還漏掉了一個重要的view—-keyguard_bouncer,它不是直接在layout佈局里加入的,只有用戶設置鎖屏保護後纔可見。


StatusBarWindowView

這裏主要查看一下PhoneStatusBarView,PanelHolder及KeyguardBouncer。

PhoneStatusBarView
PhoneStatusBarView即爲手機最上方的狀態欄,主要用於顯示系統狀態,通知等,主要包括 notification icons 和 status bar icons


PhoneStatusBarView

PanelHolder
PanelHolder是用戶下拉 status bar 後得到的 view。它主要包含 QuickSettings 和 Notification panel 兩個部分。
PanelHolder是一個繼承自FrameLayout的自定義view,它的內容是通過include status_bar_expanded.xml進行填充的。
PanelHolder的佈局比較複雜,爲了提高view的重用性大量的使用了include標籤。


PanelHolder

在平時修改QuickSettings以及Notification panel界面佈局及功能相關的文件分別QSPanel及NotificationPanelView。

KeyguardBouncer
KeyguardBouncer是鎖屏解鎖界面,根據用戶設置的解鎖方式不同,展示不同的解鎖模式。
先看看KeyguardBouncer長什麼樣子:

鎖屏界面:


Notification Keyguard

上滑鎖屏後:


KeyguardBouncer


需要注意的是KeyguardBouncer有多種形式,上圖中展示的是圖案解鎖,若爲密碼解鎖KeyguardBouncer將會以數字鍵盤的形式展示。但無論哪種形式,都是在KeyguardBouncer中加載進來的。

public class KeyguardBouncer { 
private ViewGroup mRoot; 
private ViewGroup mContainer; 
private KeyguardHostView mKeyguardView;
private void inflateView() {        
       mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
       mKeyguardView =(KeyguardHostView)mRoot.findViewById(R.id.keyguard_host_view);
       mKeyguardView.setLockPatternUtils(mLockPatternUtils);
       mKeyguardView.setViewMediatorCallback(mCallback); 
       mContainer.addView(mRoot,mContainer.getChildCount());
}

KeyguardBouncer的View樹如下:


KeyguardBouncer

小結:

通過分析SystemBars的呈現流程介紹了PhoneStatusBarView,PanelHolder,keyguardbouncer三個常見SystemUI的界面佈局及相關功能。當然,SystemUI的佈局還是很複雜的,上述只對主要的視圖從大的方向上做了分析。SystemUI也不只以上幾個佈局,包括最近應用視圖,底部導航欄(Navigation Bar),全局音量管理Dialog等,本文暫時未介紹到。

總結:

本文分爲兩部分,分別介紹了SystemUI主體框架啓動流程及SystemUI關鍵視圖的界面佈局呈現過程。通過以上介紹,在以後遇到SystemUI相關問題時,可以先定位出問題View屬於哪個大的分類,然後結合圖例給出的id縮小定位範圍。



作者:音蒼
鏈接:http://www.jianshu.com/p/0ab1279465fa
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

發佈了23 篇原創文章 · 獲贊 39 · 訪問量 24萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章