本文来分析 SystemUI 的 VolumeUI 模块,这个模块比较简单,它使用MVP架构完成设计的,如下图

本文首先会讲解这个架构如何形成的,然后会分析按下 Power 键后处理流程。
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 的构造函数
// 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 层。
在 init() 中,会向 VolumeDialogControllerImpl (Presenter层) 注册一个回调,也就是 View 层与 Presenter 层建立关联,从而可以通过 Presenter 层控制 View 层。
现在 View 层已经和 Presenter 层关联了,那么 Model 层呢?还记得前面提到的启动 VolumeUI 功能的代码吗?它调用的是 VolumeDialogComponent#register(),它完成的就是 Model 层与 Presenter 的关联,具体调用的是 VolumeDialogControllerImpl#register()
// 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 这一环,当然这只是我个人想法。