本文来分析 SystemUI 的 GlobalAction 模块,GlobalAction 这个名字一听,就感觉范围很广,但是实际只处理了长按 Power 键这个功能。
长按 Power 键首先是需要经过底层处理,然后调用上层的 PhoneWindowManager 来处理,但限于本人的知识范畴,本文将从 PhoneWindowManager 说起。
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
switch (keyCode) {
case KeyEvent.KEYCODE_POWER: {
// ...
if (down) {
// 按下Power会向Handler发送一个长按消息
interceptPowerKeyDown(event, interactive);
} else {
//
interceptPowerKeyUp(event, interactive, canceled);
}
break;
}
}
}
Power 事件不会传给 Activity来处理,而是由 PhoneWindowManager 自己处理,它会向 Handler 发送一个消息,最终通过 powerLongPress() 来处理。
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
private void powerLongPress() {
final int behavior = getResolvedLongPressOnPowerBehavior();
switch (behavior) {
case LONG_PRESS_POWER_GLOBAL_ACTIONS:
showGlobalActionsInternal();
break;
}
}
void showGlobalActionsInternal() {
if (mGlobalActions == null) {
// mWindowManagerFuncs 由 WindowManagerService 实现
mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
}
final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
}
PhoneWindowManager 把处理长按 Power 的任务交给了 GlobalActions。
GlobalActions 会监听 SystemUI 的状态,从而决定长按 Power 键的处理流程。所以 GlobalActions 相当于一个策略类。
// frameworks/base/services/core/java/com/android/server/policy/GlobalActions.java
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
// ...
// GlobalActions在构造函数中会向StatusBarManagerService注册一个监听
// 当SystemUI就绪后,mGlobalActionsAvailable的值就为true
if (mGlobalActionsAvailable) {
// mGlobalActionsProvider 是由StatusBarManagerService创建并保存的
mGlobalActionsProvider.showGlobalActions();
}
}
经过 GlobalActions 的决策,它把这个任务又交给了 StatusBarManagerService 了。这一次终于和 SystemUI 搭上线了。
// frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
private final GlobalActionsProvider mGlobalActionsProvider = new GlobalActionsProvider() {
@Override
public void showGlobalActions() {
if (mBar != null) {
try {
// mBar是StatusBar注册的服务CommandQueue
mBar.showGlobalActionsMenu();
} catch (RemoteException ex) {}
}
}
}
StatusBarManagerService 是一个服务端,它通知了客户端 StatusBar。
在 StatusBar 启动的时候,它会向 StatusBarManagerService 注册一个服务 CommandQueue,也就是这里的
mBar变量。
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@Override
public void showGlobalActionsMenu() {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_GLOBAL_ACTIONS);
// 通过handleShowGlobalActionsMenu()回调给注册者
mHandler.obtainMessage(MSG_SHOW_GLOBAL_ACTIONS).sendToTarget();
}
}
从前面几篇文章可以发现,SystemUI 的很多模块都向 CommandQueue 注册了回调。当 StatusBarManagerService 向 CommandQueue 回调后,它会通知注册回调的模块,这里它会回调 handleShowGlobalActionsMenu(),而它是由 SystemUI 的一个服务实现,这个服务就是 GlobalActionsComponent,也就是本文的主角。
// frameworks/base/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@Override
public void handleShowGlobalActionsMenu() {
// 交给了GlobalActionsImpl
mExtension.get().showGlobalActions(this);
}
GlobalActionsComponent 一看名字就是代表 SystemUI 的一个模块,它会把任务交给 GlobalActionsImpl 来处理。
前面分析 VolumeUI 也可以发现,VolumeDialogComponent 代表 VolumeUI 模块,它把具体任务也是交给了 VolumeDialogImpl 处理。
// frameworks/base/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@Override
public void showGlobalActions(GlobalActionsManager manager) {
if (mDisabled) return;
if (mGlobalActions == null) {
mGlobalActions = new GlobalActionsDialog(mContext, manager);
}
mGlobalActions.showDialog(mKeyguardMonitor.isShowing(),
mDeviceProvisionedController.isDeviceProvisioned(),
mPanelExtension.get());
}
可以发现显示 Dialog UI 的操作,交给了 GlobalActionsDialog。
GlobalActionsDialog 严格来说,并不完全是 MVP 架构中的 Presenter 角色,它自己还处理了关机UI的显示。个人觉得应该把这个逻辑还是交给 GlobalActionsDialog,因为它是真正意义上的 View 层。
// frameworks/base/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
public void showDialog(boolean keyguardShowing, boolean isDeviceProvisioned,
GlobalActionsPanelPlugin panelPlugin) {
// ...
if (mDialog != null) {
} else {
handleShow();
}
}
private void handleShow() {
awakenIfNecessary();
// 创建Dialog
mDialog = createDialog();
prepareDialog();
if (mAdapter.getCount() == 1
&& mAdapter.getItem(0) instanceof SinglePressAction
&& !(mAdapter.getItem(0) instanceof LongPressAction)) {
// 如果只有一个选项,立即执行
((SinglePressAction) mAdapter.getItem(0)).onPress();
} else {
WindowManager.LayoutParams attrs = mDialog.getWindow().getAttributes();
attrs.setTitle("ActionsDialog");
attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mDialog.getWindow().setAttributes(attrs);
// 如果有多个选项,就显示一个Dialog
mDialog.show();
// 通知WindowManagerService
mWindowManagerFuncs.onGlobalActionsShown();
}
}
从这里就可以清晰的看到创建Dialog到显示的过程,细节就不深究了。