SystemUI之VolumeUI分析

本文來分析 SystemUI 的 VolumeUI 模塊,這個模塊比較簡單,它使用MVP架構完成設計的,以下圖java

MVP

本文首先會講解這個架構如何造成的,而後會分析按下 Power 鍵後處理流程。android

MVP的建立

經過 SystemUI之StatusBar建立 可知,VolumeUI 的入口爲 VolumeUI#start()設計模式

// frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
    
    public void start() {
        // ...

        // 建立 VolumeDialogComponent 對象
        mVolumeComponent = SystemUIFactory.getInstance()
                .createVolumeDialogComponent(this, mContext);
        mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
        
        // ...
        
        // 啓動VolumeUI的功能
        mVolumeComponent.register();
    }
複製代碼

VolumeUI 啓動的時候會建立一個 VolumeDialogComponent 對象,從名字能夠看出,它表明 VolumeUI 組件,經過它能夠建立整個MVP。架構

VolumeDialogComponent 對象建立完成後,就會調用它的register()方法啓動 VolumeUI 功能。它其實就是關聯 Presenter 層和 Model 層。框架

首先來看看 VolumeDialogComponent 的構造函數ide

// frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
    
    public VolumeDialogComponent(SystemUI sysui, Context context) {
        // ...

        // build()以後,會調用createDefault(),而後調用Callback
        Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class)
                .withPlugin(VolumeDialog.class)
                .withDefault(this::createDefault)
                .withCallback(dialog -> {
                    if (mDialog != null) {
                        mDialog.destroy();
                    }
                    mDialog = dialog;
                    mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
                }).build();

        // ...省略了Volume Policy功能的代碼
    }
    
    protected VolumeDialog createDefault() {
        VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
        impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
        impl.setAutomute(true);
        impl.setSilentMode(false);
        return impl;
    }    
複製代碼

VolumeDialogComponent 經過 createDefault() 建立 VolumeDialogImpl 對象,它表明 View 層,而後經過init() 進行了初始化。函數

// frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
    
    public VolumeDialogImpl(Context context) {
        // VolumeDialogControllerImpl
        mController = Dependency.get(VolumeDialogController.class);
    }
    
    public void init(int windowType, Callback callback) {
        // 建立Dialog並設置參數
        initDialog();
        // 設置回調
        mController.addCallback(mControllerCallbackH, mHandler);
    }  
複製代碼

在 VolumeDialogImpl (View層)的構造函數中,建立了 VolumeDialogControllerImpl 對象,它表明了 Presenter 層。post

在 init() 中,會向 VolumeDialogControllerImpl (Presenter層) 註冊一個回調,也就是 View 層與 Presenter 層創建關聯,從而能夠經過 Presenter 層控制 View 層。ui

如今 View 層已經和 Presenter 層關聯了,那麼 Model 層呢?還記得前面提到的啓動 VolumeUI 功能的代碼嗎?它調用的是 VolumeDialogComponent#register(),它完成的就是 Model 層與 Presenter 的關聯,具體調用的是 VolumeDialogControllerImpl#register()this

// frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java

    protected final VC mVolumeController = new VC();
    
    public void register() {
        try {
            // 向Audio Manager註冊了一個Binder,其實就是一個回調
            mAudio.setVolumeController(mVolumeController);
        } catch (SecurityException e) {
            Log.w(TAG, "Unable to set the volume controller", e);
            return;
        }
    }
複製代碼

Audio Manager 就是 Model 層,VolumeDialogControllerImpl 向 Audio Manager 註冊了一個回調,其實就是 Presenter 層與 Model 層的關聯。

音量UI顯示

如今MVP框架已經造成,如今就來分析下當按下 Power 鍵後,VolumeUI 是如何顯示UI的。

因爲 VolumeDialogControllerImpl 向Audio Manager註冊了回調,當按下音量鍵調整了音量後,VolumeDialogControllerImpl 就會收到回調

// frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
    
    private final class VC extends IVolumeController.Stub {
        @Override
        public void volumeChanged(int streamType, int flags) throws RemoteException {
            // 調用 onVolumeChangedW()
            mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
        }
    }

    boolean onVolumeChangedW(int stream, int flags) {
        final boolean showUI = shouldShowUI(flags);
        final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
        final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
        final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
        boolean changed = false;
        if (showUI) {
            changed |= updateActiveStreamW(stream);
        }
        int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
        changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
        changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
        if (changed) {
            mCallbacks.onStateChanged(mState);
        }
        if (showUI) {
            mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
        }
        if (showVibrateHint) {
            mCallbacks.onShowVibrateHint();
        }
        if (showSilentHint) {
            mCallbacks.onShowSilentHint();
        }
        return changed;
    }
複製代碼

根據 flags 決定要執行哪一個回調,若是要顯示UI,就會回調 onShowRequested() , 而這個回調固然是由 View 層實現的。

// frameworks/base/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
    
    private final VolumeDialogController.Callbacks mControllerCallbackH
            = new VolumeDialogController.Callbacks() {
        @Override
        public void onShowRequested(int reason) {
            showH(reason);
        }
    }
    
    private void showH(int reason) {
        // 顯示Dialog
        mDialog.show();
    }    
複製代碼

View 層就完成了一個 Dialog 的顯示。

一點想法 

VolumeUI 固然不僅這麼只功能,可是隻要你懂得了這個 MVP 的設計,分析其它功能就不是什麼難事。

另外呢,我在分析代碼的時候發現 VolumeDialogComponent 其實能夠省略的,它除了建立 View 層外,其實還控制着 Volume Policy 的功能,可是這個功能是由 SettingsProvider 控制的,而最終實現控制 Volume Policy 功能的是 Presenter 層。根據設計模式中的單一職責的原則,Volume Policy 的代碼能夠徹底由 Presenter 實現,從而就能夠省略 VolumeDialogComponent 這一環,固然這只是我我的想法。

相關文章
相關標籤/搜索