Android U 多任务分屏——整体流程介绍

原生的分屏功能是在多任务中,点击应用图标选择分屏,在选择多任务中的其他应用进行分屏
在这里插入图片描述

代码分析思路

进入上分屏代码分析

我们有两种分析方法,一种是通过Split screen文字查找对应的onClick事件跟踪代码流程
在这里插入图片描述
另一种是通过,点击进入到上分屏之后,Toast弹出的文字Tap another app to use split screen来分析
在这里插入图片描述

推荐使用第二种方式,如果使用第一种从点击事件开始跟踪比较复杂且容易出错,但是第二种是在操作结束的地方才会有这个打印,我们只需找到打印这个Toast的地方加堆栈即可。
这里我们以这里点击进入到分屏后,Toast弹出的文字Tap another app to use split screen来分析,在多任务中点击Split screen的流程。

字符串一般都在XML文件中,我们在代码中找下
在这里插入图片描述
我们找到toast_split_select_app,看看其有没有在java文件中调用
在这里插入图片描述
发现java文件中没有这个string,推测是在XML文件中套娃了
在这里插入图片描述
我们找到了split_instructions_view.xml
在这里插入图片描述
方法其是在SplitInstructionsView.javagetSplitInstructionsView方法里面添加的,我们在这里添加堆栈打印,就能得到进入上分屏的流程。

Launcher: 	at com.android.quickstep.views.SplitInstructionsView.getSplitInstructionsView(SplitInstructionsView.java:79)
Launcher: 	at com.android.quickstep.views.RecentsView.createInitialSplitSelectAnimation(RecentsView.java:3196)
Launcher: 	at com.android.quickstep.views.RecentsView.createTaskDismissAnimation(RecentsView.java:3493)
Launcher: 	at com.android.quickstep.views.RecentsView.createSplitSelectInitAnimation(RecentsView.java:4636)
Launcher: 	at com.android.launcher3.uioverrides.RecentsViewStateController.handleSplitSelectionState(RecentsViewStateController.java:139)
Launcher: 	at com.android.launcher3.uioverrides.RecentsViewStateController.setStateWithAnimationInternal(RecentsViewStateController.java:109)
Launcher: 	at com.android.launcher3.uioverrides.BaseRecentsViewStateController.setStateWithAnimation(BaseRecentsViewStateController.java:91)
Launcher: 	at com.android.launcher3.uioverrides.BaseRecentsViewStateController.setStateWithAnimation(BaseRecentsViewStateController.java:61)
Launcher: 	at com.android.launcher3.statemanager.StateManager.createAnimationToNewWorkspaceInternal(StateManager.java:350)
Launcher: 	at com.android.launcher3.statemanager.StateManager.goToStateAnimated(StateManager.java:285)
Launcher: 	at com.android.launcher3.statemanager.StateManager.goToState(StateManager.java:273)
Launcher: 	at com.android.launcher3.statemanager.StateManager.goToState(StateManager.java:152)
Launcher: 	at com.android.launcher3.statemanager.StateManager.goToState(StateManager.java:145)
Launcher: 	at com.android.quickstep.views.LauncherRecentsView.initiateSplitSelect(LauncherRecentsView.java:210)
Launcher: 	at com.android.quickstep.views.TaskView.initiateSplitSelect(TaskView.java:1767)
Launcher: 	at com.android.quickstep.TaskShortcutFactory$SplitSelectSystemShortcut.onClick(TaskShortcutFactory.java:124)
Launcher: 	at com.android.quickstep.views.TaskMenuView$$ExternalSyntheticLambda3.onClick(Unknown Source:2)
Launcher: 	at android.view.View.performClick(View.java:7659)
Launcher: 	at android.view.View.performClickInternal(View.java:7636)
Launcher: 	at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
Launcher: 	at android.view.View$PerformClick.run(View.java:30156)
Launcher: 	at android.os.Handler.handleCallback(Handler.java:958)
Launcher: 	at android.os.Handler.dispatchMessage(Handler.java:99)
Launcher: 	at android.os.Looper.loopOnce(Looper.java:205)
Launcher: 	at android.os.Looper.loop(Looper.java:294)
Launcher: 	at android.app.ActivityThread.main(ActivityThread.java:8177)
Launcher: 	at java.lang.reflect.Method.invoke(Native Method)
Launcher: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
Launcher: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

进入下分屏代码分析

进入下分屏的代码如何查找呢?

方法一(正向查找)

从现象上看,进入上分屏后,在多任务界面点击任意的Task,然后进入到分屏。因此可以尝试从下面两点进行分析:
1.最近任务界面有对Task的onClick事件进行监听
2.进入分屏时,涉及窗口相关变化,因此桌面进程一定会进行跨进程通信
关于第二点先进行排除,因为当前无法知道,桌面是如何跨进程通信到系统侧
因此我们需要从第一点进行排查,从第一点中,我们可以获得如下关键词
关键词:桌面进程、Task、onClick、分屏(split screen)
组织一下上述关键词就是,在桌面里面有对Task进行onClick操作之后会触发分屏(split screen)相关的方法
在源码根目录下我们可以进入到桌面相关的目录cd packages/apps/Launcher3
在这里插入图片描述

运行命令find . -iname "*task*" | xargs grep -irnozP " onClick.*[\s\S]*?split" > codeFind.txt查找相关代码,简单解释下这个语句:

  • find . -iname "*task*"
    模糊查询Task相关文件
  • xargs grep -irnozP " onClick.*[\s\S]*?split"
    拼接,使用正则表达式查询包含onClick到split的句子
    grep参数说明: -P: 启用Perl正则表达式模式;-z: 会把整个文本当成一个字符串,允许多行匹配,即使匹配跨越了换行符的行;-o: 只输出匹配的部分;-n:显示行数;-r: 递归查询; -i: 忽略大小写。
    正则表达式说明: onClick.*[\s\S]*?split onClick.*:包含以【空格】onClick开头的单词;[\s\S]*?:多行匹配,\s匹配任何空白字符,包括空格、制表符、换页符等等,\S匹配任何非空白字符;split:以split结尾的单词。

最终我们输出一个名为codeFind.txt的文件,我们之后需要在这个文件中再次筛选出我们要找的代码。
(目前没有发现别的更高效的方法,如果有别的方法可以在评论区留言)
最终我们找到
在这里插入图片描述
代码路径:packages/apps/Launcher3/quickstep/src/com/android/quickstep/views/TaskView.java

    private void onClick(View view) {
        if (getTask() == null) {
            return;
        }
        if (confirmSecondSplitSelectApp()) {
            return;
        }
        launchTasks();
        mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
                .log(LAUNCHER_TASK_LAUNCH_TAP);
    }

跟踪confirmSecondSplitSelectApp()并走读后续流程。

方法二(逆向查找)【推荐】

确认下分屏后,会进入到分屏的状态,可以通过分屏的分割线查找相关代码

图层分析

adb shell dumpsys SurfaceFlinger,找到HWC layers,对比分屏与非分屏状态的图层
在这里插入图片描述我们发现多了一个StageCoordinatorSplitDivider,其实就是分屏时的分割线
在这里插入图片描述
直接搜索StageCoordinatorSplitDivider,发现其结果为空
在这里插入图片描述
也就是说StageCoordinatorSplitDivider是拼接而成的字符串,因此分开搜索StageCoordinatorSplitDivider
在这里插入图片描述发现有StageCoordinator,还有SplitScreenController等分屏相关的java文件,先尝试在StageCoordinator中搜索SplitDivider,我们找到
在这里插入图片描述
StageCoordinator.onTaskAppeared方法从命名上看似乎是对Task进行监听的方法,这个方法里面有初始化分割线控件的方法,我们在这个SplitLayout中添加堆栈
在这里插入图片描述
运行make SystemUI命令编译文件

SplitScreen: SplitLayout 
SplitScreen: java.lang.Exception
SplitScreen: 	at com.android.wm.shell.common.split.SplitLayout.<init>(go/retraceme a9a41fad2b076569b5dcce5872086854a2b8f379c7dc07dcce654ce543da67bb:137)
SplitScreen: 	at com.android.wm.shell.splitscreen.StageCoordinator.onTaskAppeared(go/retraceme a9a41fad2b076569b5dcce5872086854a2b8f379c7dc07dcce654ce543da67bb:1762)
SplitScreen: 	at com.android.wm.shell.ShellTaskOrganizer.onTaskAppeared(go/retraceme a9a41fad2b076569b5dcce5872086854a2b8f379c7dc07dcce654ce543da67bb:490)
SplitScreen: 	at com.android.wm.shell.ShellTaskOrganizer.onTaskAppeared(go/retraceme a9a41fad2b076569b5dcce5872086854a2b8f379c7dc07dcce654ce543da67bb:477)
SplitScreen: 	at android.window.TaskOrganizer$1.lambda$onTaskAppeared$4(TaskOrganizer.java:325)
SplitScreen: 	at android.window.TaskOrganizer$1.$r8$lambda$Z3SZqVKLE-2zO9NE5htsmlBghFs(Unknown Source:0)
SplitScreen: 	at android.window.TaskOrganizer$1$$ExternalSyntheticLambda5.run(Unknown Source:6)
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)

可惜这个堆栈并不是我们想要的结果,从这个堆栈上看,没有发现分屏操作相关的流程,只是对SplitLayout的初始化。
但是我们在前面的分析过程中,发现了在SystemUI进程中分屏的两个关键类StageCoordinatorSplitScreenController

查找跨进程调用的方式

由于该功能涉及桌面和SystemUI的跨进程通信,因此我们需要在这两个类里面寻找有跨进程通信的aidl。
涉及aidl必定有其实现类,所以我们分别在这两类中搜索stub
StageCoordinator.java发现里面只有一些动画相关调用
在这里插入图片描述
而在SplitScreenController中我们找到ISplitScreen.Stub
在这里插入图片描述
这里我们同时也确认了SystemUI侧的调用位置,都是通过ISplitScreen的调用进行跨进程通信。
通过搜索ISplitScreen找到在桌面进程里面的调用位置
在这里插入图片描述找到代码:packages/apps/Launcher3/quickstep/src/com/android/quickstep/SystemUiProxy.java
还有一种更为简便的方式
ISplitScreen.aidl文件中搜索任意方法,在桌面进程里面的调用位置
例如 里面的registerSplitScreenListener方法为例
在这里插入图片描述
在这里插入图片描述我们同样找到SystemUiProxy.java
ISplitScreen.aidl中里面的方法在SystemUiProxy.java中有实现,因此我们需要看看这些实现的方法在哪调用,从而找到关键方法。
这里我们以ISplitScreen.startTasks方法为例,在SystemUiProxy.java中查找startTasks方法
在这里插入图片描述
这里可以添加堆栈进行分屏操作后查看打印,这个方法也确实是多任务启动分屏的核心方法,一次能找到纯属偶然。
所以我们这里假设这个方法不是调用分屏的核心方法,我们继续查找桌面侧该方法的相关调用

在这里插入图片描述找到packages/apps/Launcher3/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
其中里面有多处调用,最终我们找到了四处
在这里插入图片描述
分别在四个方法中添加堆栈,查找其调用流程,添加方法不再赘述,最终我们得到

Launcher: 	at com.android.quickstep.util.SplitSelectStateController.launchTasksRefactored(SplitSelectStateController.java:412)
Launcher: 	at com.android.quickstep.util.SplitSelectStateController.launchTasks(SplitSelectStateController.java:329)
Launcher: 	at com.android.quickstep.util.SplitSelectStateController.launchSplitTasks(SplitSelectStateController.java:258)
Launcher: 	at com.android.quickstep.views.RecentsView.lambda$confirmSplitSelect$34(RecentsView.java:4718)
Launcher: 	at com.android.quickstep.views.RecentsView.$r8$lambda$uSHMT-0i9azkpL6YW9McOOFyjVQ(Unknown Source:0)
Launcher: 	at com.android.quickstep.views.RecentsView$$ExternalSyntheticLambda26.accept(Unknown Source:4)
Launcher: 	at com.android.launcher3.anim.AnimatorListeners$EndStateCallbackWrapper.onAnimationEnd(AnimatorListeners.java:80)
Launcher: 	at android.animation.Animator$AnimatorListener.onAnimationEnd(Animator.java:711)
Launcher: 	at android.animation.Animator$AnimatorCaller$$ExternalSyntheticLambda1.call(Unknown Source:4)
Launcher: 	at android.animation.Animator.callOnList(Animator.java:669)
Launcher: 	at android.animation.Animator.notifyListeners(Animator.java:608)
Launcher: 	at android.animation.Animator.notifyEndListeners(Animator.java:633)
Launcher: 	at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1306)
Launcher: 	at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1566)
Launcher: 	at android.animation.ValueAnimator.pulseAnimationFrame(ValueAnimator.java:1582)
Launcher: 	at android.animation.AnimatorSet.pulseFrame(AnimatorSet.java:1314)
Launcher: 	at android.animation.AnimatorSet.handleAnimationEvents(AnimatorSet.java:1297)
Launcher: 	at android.animation.AnimatorSet.doAnimationFrame(AnimatorSet.java:1197)
Launcher: 	at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:328)
Launcher: 	at android.animation.AnimationHandler.-$$Nest$mdoAnimationFrame(Unknown Source:0)
Launcher: 	at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:86)
Launcher: 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1337)
Launcher: 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
Launcher: 	at android.view.Choreographer.doCallbacks(Choreographer.java:952)
Launcher: 	at android.view.Choreographer.doFrame(Choreographer.java:878)
Launcher: 	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
Launcher: 	at android.os.Handler.handleCallback(Handler.java:958)
Launcher: 	at android.os.Handler.dispatchMessage(Handler.java:99)
Launcher: 	at android.os.Looper.loopOnce(Looper.java:205)
Launcher: 	at android.os.Looper.loop(Looper.java:294)
Launcher: 	at android.app.ActivityThread.main(ActivityThread.java:8177)
Launcher: 	at java.lang.reflect.Method.invoke(Native Method)
Launcher: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
Launcher: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

其中Launcher: at com.android.quickstep.views.RecentsView.lambda$confirmSplitSelect$34(RecentsView.java:4718)开始lambda表达式,我们找到这里RecentsView.confirmSplitSelect方法的第4718
在这里插入图片描述只需要继续在RecentsView.confirmSplitSelect方法添加堆栈,我们得到

Launcher: 	at com.android.quickstep.views.RecentsView.confirmSplitSelect(RecentsView.java:4716)
Launcher: 	at com.android.quickstep.views.TaskView.confirmSecondSplitSelectApp(TaskView.java:762)
Launcher: 	at com.android.quickstep.views.TaskView.onClick(TaskView.java:746)
Launcher: 	at com.android.quickstep.views.TaskView.$r8$lambda$2dErZAYzRAWboZR0vn31kIz_HGY(Unknown Source:0)
Launcher: 	at com.android.quickstep.views.TaskView$$ExternalSyntheticLambda2.onClick(Unknown Source:2)
Launcher: 	at android.view.View.performClick(View.java:7659)
Launcher: 	at android.view.View.performClickInternal(View.java:7636)
Launcher: 	at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
Launcher: 	at android.view.View$PerformClick.run(View.java:30156)
Launcher: 	at android.os.Handler.handleCallback(Handler.java:958)
Launcher: 	at android.os.Handler.dispatchMessage(Handler.java:99)
Launcher: 	at android.os.Looper.loopOnce(Looper.java:205)
Launcher: 	at android.os.Looper.loop(Looper.java:294)
Launcher: 	at android.app.ActivityThread.main(ActivityThread.java:8177)
Launcher: 	at java.lang.reflect.Method.invoke(Native Method)
Launcher: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
Launcher: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

关键在这里Launcher: at com.android.quickstep.views.TaskView.onClick(TaskView.java:746)
我们终于找到点击下分屏调用的onClick方法,自此分屏的触发流程结束。

这个查找过程中我们也确定了桌面与SystemUI的跨进程通信方式,即通过ISplitScreen.aidl,SystemUI侧的流程查找不再赘述。

分割线布局

补充说明一下,还有一种方式可以更进一步的帮助我们了解其分割线布局,需要一个前置条件,即手机使用userdebug版本,如果使用模拟器的话使用下面这种方式启动即可:

#以可写的形式打开模拟器,否则模拟器文件是只读状态
./emulator '@模拟器名字' -writable-system

在AndroidStudio中,在菜单栏上选择Tools -> Legacy Layout Inspector出现如下界面在这里插入图片描述
我们选择systemui的进程

在这里插入图片描述
在这个进程中找到前面dumpsys SurfaceFlinger的这个分割线window(StageCoordinatorSplitDivider),我们可以得到这样的View树
在这里插入图片描述找的id就可以找到对应的布局,以及其调用地点
最终我们找到布局文件:
frameworks/base/libs/WindowManager/Shell/res/layout/split_divider.xml

和split_divider.xml相关的java文件:
frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java
自定义分割线控件,中间可以拖动的部分。

frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
自定义分割线控件,divider_bar的边界相关控件。本地注释掉该代码没有实际影响。

frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
自定义分割线布局,主要加载了divider_bar、docked_divider_background和docked_divider_handle。

frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
分割线控件管理,用于inflate split_divider.xml加载DividerView以及其Surface处理。

frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
分屏布局,初始化一些参数:orientation、Rotation、densityDpi、屏幕大小等屏幕相关基础属性,还有SplitWindowManager。常用于调整上下分屏比例、分屏Task的bounds等。

我们同样可以在这个类里面添加堆栈打印,查看其创建方法,这里不再赘述。

层级结构

#1 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
 #1 Task=3 type=standard mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
  #1 Task=4 type=standard mode=multi-window override-mode=multi-window requested-bounds=[0,1498][1440,2960] bounds=[0,1498][1440,2960]
   #0 Task=55 type=standard mode=multi-window override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,1498][1440,2960]
    #0 ActivityRecord{c8a85d8 u0 com.android.dialer/.main.impl.MainActivity t55} type=standard mode=multi-window override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,1498][1440,2960]
     #0 17b29c2 com.android.dialer/com.android.dialer.main.impl.MainActivity type=standard mode=multi-window override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,1498][1440,2960]
  #0 Task=5 type=standard mode=multi-window override-mode=multi-window requested-bounds=[0,0][1440,1463] bounds=[0,0][1440,1463]
   #0 Task=61 type=standard mode=multi-window override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,1463]
    #0 ActivityRecord{a029103 u0 com.android.messaging/.ui.conversationlist.ConversationListActivity t61} type=standard mode=multi-window override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,1463]
     #0 d3dd038 com.android.messaging/com.android.messaging.ui.conversationlist.ConversationListActivity type=standard mode=multi-window override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,1463]

对应如下树状图
在这里插入图片描述

各模块责任与启动分屏堆栈

Launcher

负责上下分屏task的确定

1.确定分屏上部分,做好相关显示动画,等待选择下分屏

Launcher: 	at com.android.quickstep.views.RecentsView.createInitialSplitSelectAnimation(RecentsView.java:3196)
Launcher: 	at com.android.quickstep.views.RecentsView.createTaskDismissAnimation(RecentsView.java:3493)
Launcher: 	at com.android.quickstep.views.RecentsView.createSplitSelectInitAnimation(RecentsView.java:4636)
Launcher: 	at com.android.launcher3.uioverrides.RecentsViewStateController.handleSplitSelectionState(RecentsViewStateController.java:139)
Launcher: 	at com.android.launcher3.uioverrides.RecentsViewStateController.setStateWithAnimationInternal(RecentsViewStateController.java:109)
Launcher: 	at com.android.launcher3.uioverrides.BaseRecentsViewStateController.setStateWithAnimation(BaseRecentsViewStateController.java:91)
Launcher: 	at com.android.launcher3.uioverrides.BaseRecentsViewStateController.setStateWithAnimation(BaseRecentsViewStateController.java:61)
Launcher: 	at com.android.launcher3.statemanager.StateManager.createAnimationToNewWorkspaceInternal(StateManager.java:350)
Launcher: 	at com.android.launcher3.statemanager.StateManager.goToStateAnimated(StateManager.java:285)
Launcher: 	at com.android.launcher3.statemanager.StateManager.goToState(StateManager.java:273)
Launcher: 	at com.android.launcher3.statemanager.StateManager.goToState(StateManager.java:152)
Launcher: 	at com.android.launcher3.statemanager.StateManager.goToState(StateManager.java:145)
Launcher: 	at com.android.quickstep.views.LauncherRecentsView.initiateSplitSelect(LauncherRecentsView.java:210)
Launcher: 	at com.android.quickstep.views.TaskView.initiateSplitSelect(TaskView.java:1767)
Launcher: 	at com.android.quickstep.TaskShortcutFactory$SplitSelectSystemShortcut.onClick(TaskShortcutFactory.java:124)
Launcher: 	at com.android.quickstep.views.TaskMenuView$$ExternalSyntheticLambda3.onClick(Unknown Source:2)
Launcher: 	at android.view.View.performClick(View.java:7659)
Launcher: 	at android.view.View.performClickInternal(View.java:7636)
Launcher: 	at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
Launcher: 	at android.view.View$PerformClick.run(View.java:30156)
Launcher: 	at android.os.Handler.handleCallback(Handler.java:958)
Launcher: 	at android.os.Handler.dispatchMessage(Handler.java:99)
Launcher: 	at android.os.Looper.loopOnce(Looper.java:205)
Launcher: 	at android.os.Looper.loop(Looper.java:294)
Launcher: 	at android.app.ActivityThread.main(ActivityThread.java:8177)
Launcher: 	at java.lang.reflect.Method.invoke(Native Method)
Launcher: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
Launcher: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

2.确定选择下分屏,播放相关动画,调用SystemUI接口进入到真正分屏

Launcher: 	at com.android.quickstep.views.RecentsView.confirmSplitSelect(RecentsView.java:4716)
Launcher: 	at com.android.quickstep.views.TaskView.confirmSecondSplitSelectApp(TaskView.java:762)
Launcher: 	at com.android.quickstep.views.TaskView.onClick(TaskView.java:746)
Launcher: 	at com.android.quickstep.views.TaskView.$r8$lambda$2dErZAYzRAWboZR0vn31kIz_HGY(Unknown Source:0)
Launcher: 	at com.android.quickstep.views.TaskView$$ExternalSyntheticLambda2.onClick(Unknown Source:2)
Launcher: 	at android.view.View.performClick(View.java:7659)
Launcher: 	at android.view.View.performClickInternal(View.java:7636)
Launcher: 	at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
Launcher: 	at android.view.View$PerformClick.run(View.java:30156)
Launcher: 	at android.os.Handler.handleCallback(Handler.java:958)
Launcher: 	at android.os.Handler.dispatchMessage(Handler.java:99)
Launcher: 	at android.os.Looper.loopOnce(Looper.java:205)
Launcher: 	at android.os.Looper.loop(Looper.java:294)
Launcher: 	at android.app.ActivityThread.main(ActivityThread.java:8177)
Launcher: 	at java.lang.reflect.Method.invoke(Native Method)
Launcher: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
Launcher: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
pendingAnimation.addEndListener(aBoolean -> {
            mSplitSelectStateController.launchSplitTasks(
                    aBoolean1 -> RecentsView.this.resetFromSplitSelectionState());
            InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
        });
Launcher: 	at com.android.quickstep.util.SplitSelectStateController.launchTasksRefactored(SplitSelectStateController.java:412)
Launcher: 	at com.android.quickstep.util.SplitSelectStateController.launchTasks(SplitSelectStateController.java:329)
Launcher: 	at com.android.quickstep.util.SplitSelectStateController.launchSplitTasks(SplitSelectStateController.java:258)
Launcher: 	at com.android.quickstep.views.RecentsView.lambda$confirmSplitSelect$34(RecentsView.java:4718)
Launcher: 	at com.android.quickstep.views.RecentsView.$r8$lambda$uSHMT-0i9azkpL6YW9McOOFyjVQ(Unknown Source:0)
Launcher: 	at com.android.quickstep.views.RecentsView$$ExternalSyntheticLambda26.accept(Unknown Source:4)
Launcher: 	at com.android.launcher3.anim.AnimatorListeners$EndStateCallbackWrapper.onAnimationEnd(AnimatorListeners.java:80)
Launcher: 	at android.animation.Animator$AnimatorListener.onAnimationEnd(Animator.java:711)
Launcher: 	at android.animation.Animator$AnimatorCaller$$ExternalSyntheticLambda1.call(Unknown Source:4)
Launcher: 	at android.animation.Animator.callOnList(Animator.java:669)
Launcher: 	at android.animation.Animator.notifyListeners(Animator.java:608)
Launcher: 	at android.animation.Animator.notifyEndListeners(Animator.java:633)
Launcher: 	at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1306)
Launcher: 	at android.animation.ValueAnimator.doAnimationFrame(ValueAnimator.java:1566)
Launcher: 	at android.animation.ValueAnimator.pulseAnimationFrame(ValueAnimator.java:1582)
Launcher: 	at android.animation.AnimatorSet.pulseFrame(AnimatorSet.java:1314)
Launcher: 	at android.animation.AnimatorSet.handleAnimationEvents(AnimatorSet.java:1297)
Launcher: 	at android.animation.AnimatorSet.doAnimationFrame(AnimatorSet.java:1197)
Launcher: 	at android.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:328)
Launcher: 	at android.animation.AnimationHandler.-$$Nest$mdoAnimationFrame(Unknown Source:0)
Launcher: 	at android.animation.AnimationHandler$1.doFrame(AnimationHandler.java:86)
Launcher: 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1337)
Launcher: 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
Launcher: 	at android.view.Choreographer.doCallbacks(Choreographer.java:952)
Launcher: 	at android.view.Choreographer.doFrame(Choreographer.java:878)
Launcher: 	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
Launcher: 	at android.os.Handler.handleCallback(Handler.java:958)
Launcher: 	at android.os.Handler.dispatchMessage(Handler.java:99)
Launcher: 	at android.os.Looper.loopOnce(Looper.java:205)
Launcher: 	at android.os.Looper.loop(Looper.java:294)
Launcher: 	at android.app.ActivityThread.main(ActivityThread.java:8177)
Launcher: 	at java.lang.reflect.Method.invoke(Native Method)
Launcher: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
Launcher: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

SystemUiProxy调用ISplitScreen.aidl进行跨进程通信到SystemUI进程
在这里插入图片描述

SystemUI

负责管理分屏task(task相关的操作会发送给systemui_server侧处理),以及分屏分割线的绘制控制。

SplitScreenController实现了ISplitScreen.aidl
在这里插入图片描述

SplitScreen: 	at android.window.WindowOrganizer.startNewTransition(WindowOrganizer.java:94)
SplitScreen: 	at com.android.wm.shell.transition.Transitions.startTransition(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561:1121)
SplitScreen: 	at com.android.wm.shell.splitscreen.SplitScreenTransitions.startEnterTransition(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561:345)
SplitScreen: 	at com.android.wm.shell.splitscreen.StageCoordinator.startWithTask(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561:701)
SplitScreen: 	at com.android.wm.shell.splitscreen.StageCoordinator.startTasks(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561:623)
SplitScreen: 	at com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl.lambda$startTasks$11(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561:1157)
SplitScreen: 	at com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl.$r8$lambda$UuHBVv3bzz4CDOdyAqlv4kaRHgY(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561:0)
SplitScreen: 	at com.android.wm.shell.splitscreen.SplitScreenController$ISplitScreenImpl$$ExternalSyntheticLambda14.accept(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561:0)
SplitScreen: 	at com.android.wm.shell.common.ExecutorUtils.lambda$executeRemoteCallWithTaskPermission$1(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561:60)
SplitScreen: 	at com.android.wm.shell.common.ExecutorUtils.$r8$lambda$s8eUOdyrqpqzzyFwAMGxO-MaCg4(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561:0)
SplitScreen: 	at com.android.wm.shell.common.ExecutorUtils$$ExternalSyntheticLambda1.run(go/retraceme d8ee0e3eecdad3cdc77c111ff5591ebe26372c47d62ba59187c5ecf96366b561: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)

1.对上分屏的Task进行启动

2.设置好对应的bound到configration中

3.对分屏的RootTask进行排位到最顶部

4.对下分屏的Task进行启动

对Task的操作均会保存到WindowContainerTransaction对象中,最后通过WindowOrganizer调用IWindowOrganizerController.aidl进行跨进程通信到SystemServer进程进程Task操作
在这里插入图片描述

注:SplitLayout初始化分屏的分割线的布局的流程,从代码上看,如果shell动画开关是打开的,那么该流程在1和2之间执行;反之,则会在4之后,即窗口relayout流程中通过shell动画流程调用。

SystemServer

仅负责systemui传递过来的命令来操作task

在这里插入图片描述

WindowOrganizerController: 	at com.android.server.wm.WindowOrganizerController.applyTransaction(WindowOrganizerController.java:597)
WindowOrganizerController: 	at com.android.server.wm.WindowOrganizerController.applyTransaction(WindowOrganizerController.java:484)
WindowOrganizerController: 	at com.android.server.wm.WindowOrganizerController.applyTransaction(WindowOrganizerController.java:502)
WindowOrganizerController: 	at com.android.server.wm.WindowOrganizerController.lambda$startTransition$3(WindowOrganizerController.java:314)
WindowOrganizerController: 	at com.android.server.wm.WindowOrganizerController.$r8$lambda$v_6sOG-rerjgO2wQxXrUcgpnWaU(WindowOrganizerController.java:0)
WindowOrganizerController: 	at com.android.server.wm.WindowOrganizerController$$ExternalSyntheticLambda3.onCollectStarted(R8$$SyntheticClass:0)
WindowOrganizerController: 	at com.android.server.wm.TransitionController.startCollectOrQueue(TransitionController.java:1306)
WindowOrganizerController: 	at com.android.server.wm.WindowOrganizerController.startTransition(WindowOrganizerController.java:310)
WindowOrganizerController: 	at com.android.server.wm.WindowOrganizerController.startNewTransition(WindowOrganizerController.java:271)
WindowOrganizerController: 	at android.window.IWindowOrganizerController$Stub.onTransact(IWindowOrganizerController.java:245)
WindowOrganizerController: 	at com.android.server.wm.WindowOrganizerController.onTransact(WindowOrganizerController.java:188)
WindowOrganizerController: 	at android.os.Binder.execTransactInternal(Binder.java:1344)
WindowOrganizerController: 	at android.os.Binder.execTransact(Binder.java:1275)

WindowOrganizerController实现了IWindowOrganizerController.aidl
其接收SystemUI中的命令来对Task进行不同的操作
applyTransaction中对Task区域大小bounds变化以及RootTask的reorder和startTask等操作进行了处理

分屏退出流程

根据shell动画开关persist.wm.debug.shell_transit配置项值(默认值为1)的不同,所触发的流程不同。
通过命令adb shell setprop persist.wm.debug.shell_transit 1可以用来设置配置项的值。
分屏退出的操作在SystemUI侧先处理,然后再到system_server侧对task进行处理。

shell动画开启(SystemUI侧)

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)

使用分割线拖拽退出

分屏状态下分割线拖动至某个阈值时会退出分屏

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)

启动某个分屏中的应用退出

这种场景一般是在分屏场景下,按HOME键回到桌面,然后再点启动之前分屏中的任意一个应用时,此时的应用会全屏显示而非分屏,会触发该退出流程。

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动画关闭(SystemUI侧)

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)

使用分割线拖拽退出

分屏状态下分割线拖动至某个阈值时会退出分屏

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键回到桌面,然后再点启动之前分屏中的任意一个应用时,此时的应用会全屏显示而非分屏,会触发该退出流程。

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)

stage的reparentTasks(system_server侧)

无论是哪种退出方式,最终都会通过SideStage.removeAllTasksMainStage.deactivate分别reparentTask。

这里分屏的退出并不会移除创建好的分屏task,reparentTask操作只是把上下分屏Task下挂载的Task移动到DefaultTaskDisplayArea下挂载。

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。

交互简图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值