序
根据shell动画开关persist.wm.debug.shell_transit
配置项值(默认值为1)的不同,所触发的流程不同。
通过命令adb shell setprop persist.wm.debug.shell_transit 1
可以用来设置配置项的值。
分屏退出的操作在SystemUI侧先处理,然后再到system_server侧对task进行处理。
通过返回键退出分屏
按下back键退出分屏
shell动画开启
persist.wm.debug.shell_transit
值为1(默认)
SplitScreen: prepareExitSplitScreen stageToTop:1
SplitScreen: java.lang.Exception
SplitScreen: at com.android.wm.shell.splitscreen.StageCoordinator.prepareExitSplitScreen(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1518)
SplitScreen: at com.android.wm.shell.splitscreen.StageCoordinator.handleRequest(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:2410)
SplitScreen: at com.android.wm.shell.transition.Transitions.requestStartTransition(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1075)
SplitScreen: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.lambda$requestStartTransition$1(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1360)
SplitScreen: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.$r8$lambda$FkH1M0vUh3zDx8R5iMruInPEXLI(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
SplitScreen: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl$$ExternalSyntheticLambda1.run(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
SplitScreen: at android.os.Handler.handleCallback(Handler.java:958)
SplitScreen: at android.os.Handler.dispatchMessage(Handler.java:99)
SplitScreen: at android.os.Looper.loopOnce(Looper.java:205)
SplitScreen: at android.os.Looper.loop(Looper.java:294)
SplitScreen: at android.os.HandlerThread.run(HandlerThread.java:67)
shell动画关闭
persist.wm.debug.shell_transit
值为0
Splitscreen: applyExitSplitScreen
Splitscreen: java.lang.Exception
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.applyExitSplitScreen(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1361)
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.exitSplitScreen(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1356)
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.onSnappedToDismiss(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:2108)
Splitscreen: at com.android.wm.shell.common.split.SplitLayout.lambda$flingDividerToDismiss$3(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:594)
Splitscreen: at com.android.wm.shell.common.split.SplitLayout.$r8$lambda$700WKN2Dd7W4zPNbeSIsGlBkTTo(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
Splitscreen: at com.android.wm.shell.common.split.SplitLayout$$ExternalSyntheticLambda5.run(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
Splitscreen: at com.android.wm.shell.common.split.SplitLayout$1.onAnimationEnd(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:633)
Splitscreen: at android.animation.Animator$AnimatorListener.onAnimationEnd(Animator.java:711)
Splitscreen: at android.animation.Animator$AnimatorCaller$$ExternalSyntheticLambda1.call(Unknown Source:4)
Splitscreen: at android.animation.Animator.callOnList(Animator.java:669)
Splitscreen: at android.animation.Animator.notifyListeners(Animator.java:608)
Splitscreen: at android.animation.Animator.notifyEndListeners(Animator.java:633)
Splitscreen: at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1306)
Splitscreen: at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1566)
Splitscreen: at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:328)
Splitscreen: at android.animation.AnimationHandler.-$$Nest$mdoAnimationFrame(Unknown Source:0)
Splitscreen: at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:86)
Splitscreen: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1337)
Splitscreen: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
Splitscreen: at android.view.Choreographer.doCallbacks(Choreographer.java:952)
Splitscreen: at android.view.Choreographer.doFrame(Choreographer.java:878)
Splitscreen: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
Splitscreen: at android.os.Handler.handleCallback(Handler.java:958)
Splitscreen: at android.os.Handler.dispatchMessage(Handler.java:99)
Splitscreen: at android.os.Looper.loopOnce(Looper.java:205)
Splitscreen: at android.os.Looper.loop(Looper.java:294)
使用分割线拖拽退出分屏
分屏状态下分割线拖动至某个阈值时会退出分屏
shell动画开启
persist.wm.debug.shell_transit
值为1(默认)
SplitScreen: prepareExitSplitScreen stageToTop:1
SplitScreen: java.lang.Exception
SplitScreen: at com.android.wm.shell.splitscreen.StageCoordinator.prepareExitSplitScreen(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1518)
SplitScreen: at com.android.wm.shell.splitscreen.StageCoordinator.onSnappedToDismiss(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:2119)
SplitScreen: at com.android.wm.shell.common.split.SplitLayout.lambda$snapToTarget$1(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:535)
SplitScreen: at com.android.wm.shell.common.split.SplitLayout.$r8$lambda$mzgqhbOmxb8OeJ0DEzq0VaaDWTg(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
SplitScreen: at com.android.wm.shell.common.split.SplitLayout$$ExternalSyntheticLambda2.run(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
SplitScreen: at com.android.wm.shell.common.split.SplitLayout$1.onAnimationEnd(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:633)
SplitScreen: at android.animation.Animator$AnimatorListener.onAnimationEnd(Animator.java:711)
SplitScreen: at android.animation.Animator$AnimatorCaller$$ExternalSyntheticLambda1.call(Unknown Source:4)
SplitScreen: at android.animation.Animator.callOnList(Animator.java:669)
SplitScreen: at android.animation.Animator.notifyListeners(Animator.java:608)
SplitScreen: at android.animation.Animator.notifyEndListeners(Animator.java:633)
SplitScreen: at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1306)
SplitScreen: at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1566)
SplitScreen: at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:328)
SplitScreen: at android.animation.AnimationHandler.-$$Nest$mdoAnimationFrame(Unknown Source:0)
SplitScreen: at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:86)
SplitScreen: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1337)
SplitScreen: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
SplitScreen: at android.view.Choreographer.doCallbacks(Choreographer.java:952)
SplitScreen: at android.view.Choreographer.doFrame(Choreographer.java:878)
SplitScreen: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
SplitScreen: at android.os.Handler.handleCallback(Handler.java:958)
SplitScreen: at android.os.Handler.dispatchMessage(Handler.java:99)
SplitScreen: at android.os.Looper.loopOnce(Looper.java:205)
SplitScreen: at android.os.Looper.loop(Looper.java:294)
SplitScreen: at android.os.HandlerThread.run(HandlerThread.java:67)
shell动画关闭
persist.wm.debug.shell_transit
值为0
Splitscreen: applyExitSplitScreen
Splitscreen: java.lang.Exception
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.applyExitSplitScreen(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1361)
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.exitSplitScreen(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1356)
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.onSnappedToDismiss(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:2108)
Splitscreen: at com.android.wm.shell.common.split.SplitLayout.lambda$snapToTarget$0(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:530)
Splitscreen: at com.android.wm.shell.common.split.SplitLayout.$r8$lambda$oU9r3E0xmWtjVrFVXY5B4lkKX7c(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
Splitscreen: at com.android.wm.shell.common.split.SplitLayout$$ExternalSyntheticLambda1.run(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
Splitscreen: at com.android.wm.shell.common.split.SplitLayout$1.onAnimationEnd(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:633)
Splitscreen: at android.animation.Animator$AnimatorListener.onAnimationEnd(Animator.java:711)
Splitscreen: at android.animation.Animator$AnimatorCaller$$ExternalSyntheticLambda1.call(Unknown Source:4)
Splitscreen: at android.animation.Animator.callOnList(Animator.java:669)
Splitscreen: at android.animation.Animator.notifyListeners(Animator.java:608)
Splitscreen: at android.animation.Animator.notifyEndListeners(Animator.java:633)
Splitscreen: at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1306)
Splitscreen: at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1566)
Splitscreen: at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:328)
Splitscreen: at android.animation.AnimationHandler.-$$Nest$mdoAnimationFrame(Unknown Source:0)
Splitscreen: at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:86)
Splitscreen: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1337)
Splitscreen: at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
Splitscreen: at android.view.Choreographer.doCallbacks(Choreographer.java:952)
Splitscreen: at android.view.Choreographer.doFrame(Choreographer.java:878)
Splitscreen: at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
Splitscreen: at android.os.Handler.handleCallback(Handler.java:958)
Splitscreen: at android.os.Handler.dispatchMessage(Handler.java:99)
Splitscreen: at android.os.Looper.loopOnce(Looper.java:205)
Splitscreen: at android.os.Looper.loop(Looper.java:294)
Splitscreen: at android.os.HandlerThread.run(HandlerThread.java:67)
启动某个分屏中的应用退出分屏
这种场景一般是在分屏场景下,按HOME键回到桌面,然后再点启动之前分屏中的任意一个应用时,此时的应用会全屏显示而非分屏,会触发该退出流程。或者直接在分屏页面下拉控制中心后,启动设置等操作。
shell动画开启
persist.wm.debug.shell_transit
值为1(默认)
SplitScreen: prepareExitSplitScreen stageToTop:-1
SplitScreen: java.lang.Exception
SplitScreen: at com.android.wm.shell.splitscreen.StageCoordinator.prepareExitSplitScreen(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1518)
SplitScreen: at com.android.wm.shell.splitscreen.StageCoordinator.handleRequest(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:2442)
SplitScreen: at com.android.wm.shell.transition.Transitions.dispatchRequest(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:931)
SplitScreen: at com.android.wm.shell.transition.DefaultMixedHandler.handleRequest(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:195)
SplitScreen: at com.android.wm.shell.transition.Transitions.requestStartTransition(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1075)
SplitScreen: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.lambda$requestStartTransition$1(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1360)
SplitScreen: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.$r8$lambda$FkH1M0vUh3zDx8R5iMruInPEXLI(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
SplitScreen: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl$$ExternalSyntheticLambda1.run(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
SplitScreen: at android.os.Handler.handleCallback(Handler.java:958)
SplitScreen: at android.os.Handler.dispatchMessage(Handler.java:99)
SplitScreen: at android.os.Looper.loopOnce(Looper.java:205)
SplitScreen: at android.os.Looper.loop(Looper.java:294)
SplitScreen: at android.os.HandlerThread.run(HandlerThread.java:67)
shell动画关闭
persist.wm.debug.shell_transit
值为0
Splitscreen: java.lang.Exception
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.applyExitSplitScreen(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1361)
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.exitSplitScreen(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:1356)
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.onStageHasChildrenChanged(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:2067)
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator.-$$Nest$monStageHasChildrenChanged(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:0)
Splitscreen: at com.android.wm.shell.splitscreen.StageCoordinator$StageListenerImpl.onStatusChanged(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:3241)
Splitscreen: at com.android.wm.shell.splitscreen.StageTaskListener.sendStatusChanged(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:447)
Splitscreen: at com.android.wm.shell.splitscreen.StageTaskListener.onTaskVanished(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:280)
Splitscreen: at com.android.wm.shell.ShellTaskOrganizer.updateTaskListenerIfNeeded(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:654)
Splitscreen: at com.android.wm.shell.ShellTaskOrganizer.onTaskInfoChanged(go/retraceme 6f05956d209492c74243b49dd6141e0b4e6a7013b057ea6bd2e2643037fbaaa6:526)
Splitscreen: at android.window.TaskOrganizer$1.lambda$onTaskInfoChanged$6(TaskOrganizer.java:340)
Splitscreen: at android.window.TaskOrganizer$1.$r8$lambda$FmJPvZyGqAGeVe9o6dSQkNL3f3g(Unknown Source:0)
Splitscreen: at android.window.TaskOrganizer$1$$ExternalSyntheticLambda3.run(Unknown Source:4)
Splitscreen: at android.os.Handler.handleCallback(Handler.java:958)
Splitscreen: at android.os.Handler.dispatchMessage(Handler.java:99)
Splitscreen: at android.os.Looper.loopOnce(Looper.java:205)
Splitscreen: at android.os.Looper.loop(Looper.java:294)
Splitscreen: at android.os.HandlerThread.run(HandlerThread.java:67)
代码分析
从上面堆栈中可以看出,这个方法中会根据配置项persist.wm.debug.shell_transit
的值进行不同的退出方法处理。
最终退出分屏的方法分为StageCoordinator.prepareExitSplitScreen
和StageCoordinator.applyExitSplitScreen
根据配置项的值判断。
persist.wm.debug.shell_transit
值为1,使用prepareExitSplitScreen
退出;
persist.wm.debug.shell_transit
值为0,使用applyExitSplitScreen
退出。
这里以onSnappedToDismiss为起点分析,常用于分割线退出分屏。
以SplitLayout.snapToTarget中回调的onSnappedToDismiss方法,即分割线滑动到顶部退出分屏,为例:
public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) {
switch (snapTarget.flag) {
//FLAG_DISMISS_START:代表分割线上滑到顶部,导致上分屏退出情况。
//即在5个分割区域的最顶部松手
case FLAG_DISMISS_START:
flingDividePosition(currentPosition, snapTarget.position, FLING_RESIZE_DURATION,
() -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */,
EXIT_REASON_DRAG_DIVIDER));
break;
......
bottomOrRight
:值为false,表示是上分屏(默认情况是SideStage)退出。
EXIT_REASON_DRAG_DIVIDER
:一个字符串,表示退出分屏的理由。
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@Override
public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
//bottomOrRight传递值false,判断SideStage是否在顶部(竖屏)或者左边(横屏);反之同理。
//mSideStagePosition默认为0,SideStage在顶端;值为1时,在底端。
//主要目的就是判断是退出的是哪个stage
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
//根据mainStageToTop,获取没有退出stage
final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage;
//判断配置项persist.wm.debug.shell_transit的值
if (!ENABLE_SHELL_TRANSITIONS) {
//传递需要退出的stage,通过exitSplitScreen退出分屏
exitSplitScreen(toTopStage, reason);
return;
}
//mainStageToTop传递值false,dismissTop值为STAGE_TYPE_SIDE。
final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
final WindowContainerTransaction wct = new WindowContainerTransaction();
//重置bounds
toTopStage.resetBounds(wct);
//传递stage类型,退出分屏
prepareExitSplitScreen(dismissTop, wct);
if (mRootTaskInfo != null) {
wct.setDoNotPip(mRootTaskInfo.token);
}
//shell动画处理,并且提交WindowContainerTransaction
mSplitTransitions.startDismissTransition(wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
}
这里我们继续以bottomOrRight
值为false
代入该方法中,可以得出mainStageToTop
值为1。(不考虑上下分屏互换场景,取mSideStagePosition默认0,SPLIT_POSITION_TOP_OR_LEFT是固定值也为0)
因此toTopStage
最终获取就是MainStage
。
这里代码中很清晰的通过配置项persist.wm.debug.shell_transit
的不同进行了不同的退出分屏调用,即exitSplitScreen和prepareExitSplitScreen。
需要注意的是prepareExitSplitScreen涉及相应的WindowContainerTransaction操作,其提交是在mSplitTransitions.startDismissTransition
处理的。
下面我们分别来看看这两个方法。
exitSplitScreen
exitSplitScreen(toTopStage, reason);
这里接上述,toTopStage
值为MainStage
。
private void exitSplitScreen(@Nullable StageTaskListener childrenToTop,
@ExitReason int exitReason) {
if (!mMainStage.isActive()) return;
final WindowContainerTransaction wct = new WindowContainerTransaction();
//前面toTopStage的值传递给了childrenToTop
applyExitSplitScreen(childrenToTop, wct, exitReason);
}
private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop,
WindowContainerTransaction wct, @ExitReason int exitReason) {
if (!mMainStage.isActive() || mIsExiting) return;
......
//判断childrenToTop(当前获取的stage是否为null),或该stage下的task的ID是否有效
if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) {
......
} else {
// Expand to top side split as full screen for fading out decor animation and dismiss
// another side split(Moving its children to bottom).
mIsExiting = true;
//重置该stage的bounds
childrenToTop.resetBounds(wct);
//传递stage的task的token,通过WindowContainerTransaction的reorder方法调整stage位置
wct.reorder(childrenToTop.mRootTaskInfo.token, true);
}
//这里传递的是分屏的中task的token,主要就是把分屏task下的叶子节点重新重新分配挂载
wct.setReparentLeafTaskIfRelaunch(mRootTaskInfo.token,
false /* reparentLeafTaskIfRelaunch */);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
t.setWindowCrop(mMainStage.mRootLeash, null)
.setWindowCrop(mSideStage.mRootLeash, null);
t.hide(mMainStage.mDimLayer).hide(mSideStage.mDimLayer);
setDividerVisibility(false, t);
//判断当前stage是否为null
if (childrenToTop == null) {
t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.right);
} else {
// In this case, exit still under progress, fade out the split decor after first WCT
// done and do remaining WCT after animation finished.
childrenToTop.fadeOutDecor(() -> {
WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
mIsExiting = false;
//重新挂载MainStage下的Task,如果childrenToTop是MainStage,则在前台显示之前挂载在MainStage下的task
mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
//重新挂载SideStage下的Task,如果childrenToTop是SideStage,则在前台显示之前挂载在SideStage下的task
mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
//重新排列层级结构
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
setRootForceTranslucent(true, finishedWCT);
finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
mSyncQueue.queue(finishedWCT);
mSyncQueue.runInSync(at -> {
at.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.right);
});
onTransitionAnimationComplete();
});
}
});
Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
// Log the exit
if (childrenToTop != null) {
logExitToStage(exitReason, childrenToTop == mMainStage);
} else {
logExit(exitReason);
}
}
这个里面主要的操作其实就是重新挂载各stage下的task。
mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
我们这里childrenToTop
传递的就是MainStage,因此MainStage下的task会前台显示,这也对应了我们向上滑动,以退出SideStage的方式退出分屏后,显示仍然是MainStage下的task。
prepareExitSplitScreen
prepareExitSplitScreen(dismissTop, wct);
/**
* Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates
* an existing WindowContainerTransaction (rather than applying immediately). This is intended
* to be used when exiting split might be bundled with other window operations.
*/
void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
if (!mMainStage.isActive()) return;
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
}
这个方法就更为简单,直接调用两个Stage的移除。
分屏退出Task
退出分屏的方法分为StageCoordinator.prepareExitSplitScreen
和StageCoordinator.applyExitSplitScreen
,使用哪个方法退出,取决于persist.wm.debug.shell_transit
的值。
persist.wm.debug.shell_transit
值为1,使用prepareExitSplitScreen
退出;
persist.wm.debug.shell_transit
值为0,使用applyExitSplitScreen
退出。
无论是哪种退出方式,最终都会通过SideStage.removeAllTasks
和MainStage.deactivate
分别reparentTask,最终会在system_server 侧处理。
这里分屏的退出并不会移除创建好的分屏task,reparentTask操作只是把上下分屏Task下挂载的应用Task移动到DefaultTaskDisplayArea下挂载。
退出分屏前:
退出分屏后:
这里我们可以看到应用Task=64和Task=29不在挂载到原来的分屏Task上。
注:按返回键退出分屏,退出来Task=29的应用,因此这里没有显示该应用task。
SideStage reparentTasks
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
if (mChildrenTaskInfo.size() == 0) return false;
wct.reparentTasks(
mRootTaskInfo.token,
null /* newParent */,
null /* windowingModes */,
null /* activityTypes */,
toTop);
return true;
}
这里mRootTaskInfo.token
指的就是SideStage。
MainStage reparentTasks
代码路径:frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
void deactivate(WindowContainerTransaction wct, boolean toTop) {
if (!mIsActive) return;
mIsActive = false;
if (mRootTaskInfo == null) return;
final WindowContainerToken rootToken = mRootTaskInfo.token;
wct.reparentTasks(
rootToken,
null /* newParent */,
null /* windowingModes */,
null /* activityTypes */,
toTop);
}
这里rootToken
指的就是MainStage。
这两个方法后续会走到system_server侧,通过WindowContainerTransaction事务处理,见Android U system_server侧WindowContainerTransaction处理 中reparentTasks
相关流程。