分屏启动
桌面入口部分
桌面点击这个Split top按钮的onClick后触发自己进程的view相关的动画,对应堆栈:
1 | at com.android.quickstep.views.RecentsView.createInitialSplitSelectAnimation(RecentsView.java:2769) |
点击之后会有相关动画的准备工作:
比如选择短信,短信app会放到上面,下面多任务依旧是多任务,但是位置发生了变化。
createInitialSplitSelectAnimation是上面那个短信应用上半屏幕部分显示的动画构建
1 | /** |
此时分屏在桌面多任务完成了第一步,把TaskView放到顶部,接下来要手动选择一个下屏app,才可以构成真正意义的分屏。
底部点击TaskView触发动画流程
1 | private void onClick(View view) { |
动画结束后执行mSplitSelectStateController.launchSplitTasks,正式调用systemui相关的shell接口进行操作真正的task相关分屏业务
1 | at com.android.quickstep.SystemUiProxy.startTasksWithLegacyTransition(SystemUiProxy.java:621) |
SystemUI部分
1 | //SystemUiProxy.java |
mSplitScreen是ISplitScreen Binder接口,会调用到SystemUI所在的服务端
这里对应SystemUI端的代码:
1 | //frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java |
mMainStage和mSideStage属于和RootTask一样,分屏的RootTask一般会带两个子task,分别是mMainStage和mSideStage的RootTaskInfo。
上面注释中的代码主要做了下面几件事情:
- 分割线初始化和上下分屏的区域参数
- 分屏的RootTask进行reorder,移到最前面
- 上下分屏执行startTask
1 | //SplitLayout的init |
区域的计算确定部分,setDivideRatio方法
1 | public void setDivideRatio(float ratio) { |
已经计算好了分屏的bound后,需要把bound设置到WindowContainerTransition中进行传递:
updateWindowBounds(mSplitLayout, wct);
1 | private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { |
对分屏的RootTask进行reorder
1 |
|
对分屏Task进行startTask
1 |
|
SystemServer部分
mTaskOrganizer.applyTransaction(wct);跨进程会调用到WindowOrganizerController的applyTransaction
1 | //frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java |
总结一下systemserver端处理其实和systemui客户端一样的:
1、针对区域大小bounds的变化,对这个Change相关进行applyWindowContainerChange方法来进行处理
2、针对task相关的2类操作,reorder和startTask两类都是包装成了HierarchyOp,调用applyHierarchyOp方法来进行处理
区域大小bounds变化applyWindowContainerChange
1 |
|
applyHierarchyOp
1 | private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects, |
1 |
|
再看下最重要的moveTaskToFrontLocked方法:
1 |
|
分割线
分割线创建
DividerSnapAlgorithm是专门管理分割线位置相关的算法类
frameworks/base/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
分割线的代表类SnapTarget
1 | //DividerSnapAlgorithm内部类 |
计算分割线方法如下
1 | private void calculateTargets(boolean isHorizontalDivision, int dockedSide) { |
参数isHorizontalDivision,代表是当前分割线是横着显示还是竖着显示,竖屏分割线是横着显示isHorizontalDivision =true
1 | public DividerSnapAlgorithm(Resources res, int displayWidth, int displayHeight, int dividerSize, |
竖屏值一般是SNAP_MODE_16_9=0,横屏值一般是SNAP_ONLY_1_1=2
竖屏的3个snap的计算方法addRatio16_9Targets
1 | private void addRatio16_9Targets(boolean isHorizontalDivision, int dividerMax) { |
addNonDismissingTargets方法主要就是有了topPosition,bottomPosition,在额外加上个middle,这样整体3个SnapTarget就构造成功。创建后的分割线SnapTarget都会被放入到mTargets这个集合中去
SnapTarget对应中间位置的堆栈
1 | at com.android.internal.policy.DividerSnapAlgorithm$SnapTarget.<init>(DividerSnapAlgorithm.java:472) |
寻找合适分割线
外部传递一个比例值情况
一般这种情况是在启动分屏时候,需要设置好一个上下分屏比例ratio,主要通过如下方法:
1 | /** Updates divide position and split bounds base on the ratio within root bounds. */ |
snap方法根据传递进来的position,与mTargets集合的所有position进行比较距离,离谁最近就选谁。
分割线拖动
在DividerView的onTouch方法中
1 | public boolean onTouch(View v, MotionEvent event) { |
重点看下mSplitLayout.updateDivideBounds(position)方法
1 | /** |
applySurfaceChanges方法
1 | public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1, |
mMainStage.onResizing方法
1 |
|
松手后mSplitLayout.snapToTarget(position, snapTarget)执行
1 | /** |
分屏退出
SystemUI端的调用
1 | //frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java |
mSideStage.removeAllTasks方法
1 | boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { |
调用到了reparentTasks
1 | public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent, |
systemserver端的调用
1 | //frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java |
整个分屏结束核心流程结束,剩下的就是要进行相关resumeToActivity和ensureVisibleActivity等操作,触发生命周期的变化。