android8.0之SystemUI分析(一)

本文將比較全面的介紹SystemUI的組成,實現,佈局以及客製化方案等等。本博文基於android8.0源碼進行分析,因爲android8.0相比之前的版本SystemUI部分改動是很是大的。java

1、SystemUI組成
SystemUI包含的功能很是豐富,組成元素主要包含常見的System Bars,以及ScreenShot截屏、壁紙、最近運行的應用程序等。SystemUI也是各大安卓版本中變化比較大的一個部分。android

Status Bar
Navigation Bar
Combined Bar (主要爲Tablet設備使用)
Notifications
LockScreen
Recent (最近任務)
QuickSettings
等等
2、SystemUI實現
瞭解了SystemUI後,本文先來大概講解下StatusBar的實現流程。ide

一、應用啓動相關代碼
相關代碼主要分爲兩個部分 
1)、Service部分 
代碼路徑:framework/base/services/java/com/android/server 
2) 、應用部分 
代碼路徑:framework/base/packages/SystemUI佈局

SystemUI中SystemUIService是整個系統UI比較重要的載體,因此咱們的分析將從SystemUIService開始,而SystemUIService是從SystemServer中啓動的。關於這部分這裏不作多的分析,詳見SystemServer.java中的startSystemUi()方法。下面來看一下SystemUIServer中的onCreate()方法。ui

/*framework/base/packages/systemui/src/com/android/systemui/SystemUIService.java*/
    @Override
    public void onCreate() {
        super.onCreate();
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();this

        // For debugging RescueParty
        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
            throw new RuntimeException();
        }
    }

上面的代碼能夠看出SystemUIService中實際有效代碼只是實例化了一個SystemUIApplication對象,而且調用了startServiceIfNeeded()方法。下面來看SystemUIApplication中的具體實現邏輯。spa

    public void startServicesIfNeeded() {
        startServicesIfNeeded(SERVICES);
    }

其中SERVICES是一組全部用戶共用的SystemUI服務,以下:.net

    /**
     * The classes of the stuff to start.
     */
    private final Class<?>[] SERVICES = new Class[] {
            Dependency.class,
            NotificationChannels.class,      //通知管理
            CommandQueue.CommandQueueStart.class,
            KeyguardViewMediator.class,    //鎖屏管理
            Recents.class,    //近期應用管理,以堆疊棧的形式展示
            VolumeUI.class,    //用來展現和控制音量的變化:媒體音量、鈴聲音量和鬧鐘音量
            Divider.class,      //分屏管理
            SystemBars.class,
            StorageNotification.class,
            PowerUI.class,    //主要處理和Power相關的事件,好比省電模式切換,電池電量變化和開關屏幕等事件
            RingtonePlayer.class,     //鈴聲播放
            KeyboardUI.class,
            PipUI.class,     //提供對畫中畫模式的管理
            ShortcutKeyDispatcher.class,    //
            VendorServices.class,
            GarbageMonitor.Service.class,
            LatencyTester.class,
            GlobalActionsComponent.class,
            RoundedCorners.class,
    };debug

    /**
     * The classes of the stuff to start for each user.  This is a subset of the services listed
     * above.
     */
    private final Class<?>[] SERVICES_PER_USER = new Class[] {
            Dependency.class,
            NotificationChannels.class,
            Recents.class
    };

上面對SystemUI要啓動的一系列服務有了個基本的介紹,下面來看SystemUIApplication中是怎麼啓動這一些列服務的component

private void startServicesIfNeeded(Class<?>[] services) {
    ...
    log.traceBegin("StartServices");
    final int N = services.length;  //獲取要啓動的服務列表的長度
    for (int i = 0; i < N; i++) {
        Class<?> cl = services[i];
        if (DEBUG) Log.d(TAG, "loading: " + cl);
        log.traceBegin("StartServices" + cl.getSimpleName());
        long ti = System.currentTimeMillis();
        try {
             /* 經過SystemUIFactory來建立相應的單例 */
            Object newService = SystemUIFactory.getInstance().createInstance(cl);
            mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
        }...
        mServices[i].mContext = this;
        mServices[i].mComponents = mComponents;
        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 " + cl.getName() + " took " + ti + " ms");
        }
        if (mBootCompleted) {
            mServices[i].onBootCompleted();
        }
    }
    log .traceEnd();    ...
}

能夠看到SystemBar這個服務只是做爲了一箇中間過程,啓動了StatusBar,如今咱們開看一下StatusBar的start()方法:

@Override
public void start() {   
... //這裏面進行了StatusBar中各個組件的初始化
    mBarService = IStatusBarService.Stub.asInterface(
            ServiceManager.getService(Context.STATUS_BAR_SERVICE));
    ...
    try {
        /* 通過一系列對象的建立與初始化後,開始向StatusBarService進行註冊。這裏涉及跨進程操做,
                  於是傳遞的參數都是繼承自Parcelable的 */
        mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
                fullscreenStackBounds, dockedStackBounds);
    } ...

    createAndAddWindows();  //這裏纔是真正將Status Bar顯示出來的地方
}

這裏你們就會有疑問了,既然已經有了StatusBar了,那麼這裏忽然殺出來個StatusBarService,究竟是爲何呢? 
先來看看StatusBarService,經過Context.STATUS_BAR_SERVICE,直覺告訴咱們這個應用程序應該是在SystemServer中。咱們能夠看看是誰向SystemServer中註冊的這個服務,下面來看一下SystemUI中的代碼。

if (!disableSystemUI) {
   traceBeginAndSlog("StartStatusBarManagerService");
   try {
       statusBar = new StatusBarManagerService(context, wm);
       ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
   } catch (Throwable e) {
       reportWtf("starting StatusBarManagerService", e);  //原來StatusBarManagerService這個傢伙註冊的
   }
   traceEnd();
}

接下來進一步分析StatusBarManagerService的實現,首先來看下其中的registerStatusBar中的代碼:

    @Override
    public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
            List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
            Rect fullscreenStackBounds, Rect dockedStackBounds) {
        enforceStatusBarService();

        Slog.i(TAG, "registerStatusBar bar=" + bar);
        mBar = bar;
        try {
            mBar.asBinder().linkToDeath(new DeathRecipient() {
                @Override
                public void binderDied() {
                    mBar = null;
                    notifyBarAttachChanged();
                }
            }, 0);
        } catch (RemoteException e) {
        }
        notifyBarAttachChanged();
        synchronized (mIcons) {    //複製icon列表
            for (String slot : mIcons.keySet()) {
                iconSlots.add(slot);
                iconList.add(mIcons.get(slot));
            }
        }
        synchronized (mLock) {
            switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
            switches[1] = mSystemUiVisibility;
            switches[2] = mMenuVisible ? 1 : 0;
            switches[3] = mImeWindowVis;
            switches[4] = mImeBackDisposition;
            switches[5] = mShowImeSwitcher ? 1 : 0;
            switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
            switches[7] = mFullscreenStackSysUiVisibility;
            switches[8] = mDockedStackSysUiVisibility;
            binders.add(mImeToken);
            fullscreenStackBounds.set(mFullscreenStackBounds);
            dockedStackBounds.set(mDockedStackBounds);
        }
    }

從上面的代碼看,registerStatusBar的做用主要是:1、爲新啓動的SystemUI應用賦予當前系統的真實值(好比有多少須要顯示的圖標);2、經過成員變量mBar記錄IstatusBar對象,它在SystemUI中對應的是CommandQueue。 
下面經過本身整理的調用流程圖,你們能夠參考一下: 

相關文章
相關標籤/搜索