本文來分析 SystemUI 的 VolumeUI 模塊,這個模塊比較簡單,它使用MVP架構完成設計的,以下圖java
本文首先會講解這個架構如何造成的,而後會分析按下 Power 鍵後處理流程。android
經過 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 層的關聯。
如今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 這一環,固然這只是我我的想法。