分屏的任务管理器和虚拟按键在com/android/systemui目录下。长按多任务键时会调用PhoneStatusBar的toggleSplitScreenMode()方法。需要说明的是,多任务界面实际上是一个名叫RecentsActivity的Activity。
protected void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction)
{
if (mRecents == null) {
return;
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
//进入分屏模式,调出最近任务页面。
mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
} else {
EventBus.getDefault().send(new UndockingTaskEvent());
if (metricsUndockAction != -1) {
MetricsLogger.action(mContext, metricsUndockAction);
}
}
需要分屏时会调用Recents的dockTopTask方法,该方法内部会调用RecentsImpl的dockTopTask,该方法除了在多任务界面做出任务管理“样式”的调整以外,还会调用SystemServicesProxy.moveTaskToDockedStack,在分屏模式下打开最近任务
public void dockTopTask(int topTaskId, int dragMode,
int stackCreateMode, Rect initialBounds) {
SystemServicesProxy ssp = Recents.getSystemServices();
// Make sure we inform DividerView before we actually start the activity so we can change
// the resize mode already.
if (ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds)) {//远程调用ActivityManagerService.moveTaskToDockedStack方法
EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
showRecents(
false /* triggeredFromAltTab */,
dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
false /* animate */,
true /* launchedWhileDockingTask*/,
false /* fromHome */,
DividerView.INVALID_RECENTS_GROW_TARGET);
}}
而该进程下的RecentsActivity则负责远程与PackageManager通信,拿到应用截图,添加到界面上
private void reloadStackView() {
//省略部分代码
RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
loadOpts.runningTaskId = launchState.launchedToTaskId;
loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
loader.loadTasks(this, loadPlan, loadOpts);
TaskStack stack = loadPlan.getTaskStack();
mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0);
mRecentsView.updateStack(stack, true /* setStackViewTasks */);
// Update the nav bar scrim, but defer the animation until the enter-window event
boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null);
//省略部分代码
}
那么7.0的Activity新增的方法onMultiWindowModeChanged是在什么时候调用的呢?回到刚才的SystemServicesProxy.moveTaskToDockedStack,该方法会跨进程调用ActivityManagerService.moveTaskToDockedStack
public boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
Rect initialBounds, boolean moveHomeStackFront) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()");
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
mWindowManager.setDockedStackCreateState(createMode, initialBounds);
final boolean moved = mStackSupervisor.moveTaskToStackLocked(
taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS, "moveTaskToDockedStack",
animate, DEFER_RESUME);
if (moved) {
if (moveHomeStackFront) {
mStackSupervisor.moveHomeStackToFront("moveTaskToDockedStack");
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
return moved;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
该方法会调用StackSupervisor.moveTaskToStackLocked,在StackSuperviso中做尺寸调整等工作以外,最终调用TaskRecord.updateOverrideConfiguration()。若需要分屏,则会调用scheduleReportMultiWindowModeChanged方法
Configuration updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
//省略部分代码
if (mFullscreen != oldFullscreen) {
mService.mStackSupervisor.scheduleReportMultiWindowModeChanged(this);
}
return !mOverrideConfig.equals(oldConfig) ? mOverrideConfig : null;
}
scheduleReportMultiWindowModeChanged最终调用ActivityThreadhandleMultiWindowModeChanged,并通过appToken找到需要回调的Activity
private void handleMultiWindowModeChanged(IBinder token, boolean isInMultiWindowMode) {
final ActivityClientRecord r = mActivities.get(token);
if (r != null) {
r.activity.dispatchMultiWindowModeChanged(isInMultiWindowMode);
}
}
Activity的dispatchMultiWindowModeChanged会回调onMultiWindowModeChanged,便可进行业务处理
当前显示自己的应用页面,长按多任务键时出现分屏
onMultiWindowModeChanged(true)->onPause-onStop->onDestroy->onCreate->onStart- >onResume->onPause
分屏时长按多任务键,全屏显示自己的应用时
onStop->onDestroy->onCreate->onStart->onResume>onPause>onMultiWindowModeChanged(false)->onResume
当前显示其他应用,按多任务键出现自己的应用时
onMultiWindowModeChanged(true)->nDestroy->onCreate->onStart->onResume
参考资料
示例 | Android Developers