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 这一环,固然这只是我我的想法。

相关文章
相关标签/搜索