android9duochuangkoubiji

一、概述:

AndroidQ上面原生的多窗口功能支持四种模式:全屏、分屏、画中画、FreeForm模式。

多窗口主要涉及ActivityManagerService、WindowManagerService、Input三个模块。///

二、原理框架

Android原生多窗口是多Stack方案,即存在多个ActivityStack。ActivityStack是一个抽象的栈,每个栈都有自己的屏幕区域bound和id,Activity是以Task方式组织并放在某一个Stack中的。

AMS和WMS中对Stack分别用ActivityStack和TaskTack描述,通过StackId来映射。对Task分别用TaskRecord、Task描述,通过TaskId来映射。

Android Q新增了ActivityDisplay对象,针对每一个逻辑屏幕分配此对象,ActivityDisplay管理屏幕上每个ActivityStack显示边界以及位置,它是屏幕的抽象,用mDisplayId区分不同的屏幕,一个DisplayId对应一个ActivityDisplay,主要维护这个display的相关信息,其中保存着ActivityStack的列表ArrayList<ActivityStack> mStacks,代表该displayId上的所有activitystack,列表中保存的ActivityStack就是需要在这块屏幕上显示的,屏幕对象作为所有任务的最主要上下文,规定了任务对应窗体的显示屏幕、区域、大小,管理在此逻辑屏幕上所有任务栈的运行与销毁。

Android Q新增ActivityStack的WindowingMode与ActivityType属性,每个ActivityStack拥有此属性,启动Activity的时候系统调度在哪个ActivityStack分配任务;

每个Activity显示在所属ActivityStack的bound区域内,多个Activity显示在各自ActivityStack的bound区域内,这样就可以实现多窗口。多窗口不仅仅是控制Activity放入不同ActivityStack中,同时还要改变Activity的生命周期,即FocusActivity是resume状态,其他可见Activity是Pause状态,并不会进入Stop状态,所以他们的关系大概如下:

ActivityDisplay -> ActivityStack -> TaskRecord -> ActivityRecord

 

进入/退出多窗口按以下逻辑框架顺序进行处理

step1.调整ActivityStack栈并设置ActivityStack的WindowMode和Bounds

step2.调整TaskRecord栈并将其加入到ActivityStack的mTaskHistory中

step3.调整将ActivityRecord插入到,TaskRecord的mActivities的顶部

step4.把当前ActivityStackmoveToFront函数将自己插入到ActicityDisplay中的mStacks的顶部并修改ActivityStackSuperVisor的mTargetStack变量为当前ActivityStack

step5.添加Window到WMS

step6.过度动画及应用窗口显示

 

三、关键函数

移栈:

AMS.moveTaskToStack()

AMS.setTaskWindowingModeSplitScreenPrimary()  //该函数可将任务移动至WindowingMode与ActivityType匹配SPLIT属性栈的Stack运行

AMS.moveTopActivityToPinnedStack()

调整栈大小:

AMS.resizeDockedStack()

AMS.resizePinnedStack()

ActivityStackSupervisor.resizeStackUncheckedLocked()

Activity生命周期:

ActivityStack.resumeTopActivityInnerLocked():

Activity启动:

ActivityStarter.startActivityUnchecked()

 

四、源码分析

1、分屏模式

进入分屏有两种入口:

AMS.startActivityFromRecents()//在任务栏中拖动一个分屏应用到顶部

AMS.swapDockedAndFullscreenStack()//长按底部任务按键

以AMS.startActivityFromRecents()为例:

 

2、画中画模式

画中画即置顶Activity,Activity的窗口永远位于所有窗口之上,所属的Stack自然位于所有Stack的上面,这类特殊的Stack称为PinnedStack(id=4)。PinnedStack无法成为FocusStack,Activity也无法成为FocusActivity。

如何启动画中画,只需在AndroidManifest中声明Activity时添加android:supportsPictureInPicture="true"和android:resizeableActivity="true”属性,并调用新方法 Activity.enterPictureInPictureMode()。时序图如下:

 

PinnedStack为非Focusablestack,Activity为非focusactivity,resumeFocusedStackTopActivityLocked()只会将stack堆栈中FocusStack的focusactivity进行resume,其他任何activity均为paused或stoped状态,所以画中画Activity是处于paused状态的.

3、FreeForm模式

FreeForm模式下并不存在与之对应的多个Stack,而是只有一个FreeFormStack存放了多个Task。这种模式下决定Activity显示位置的不是Stackbound,而是Taskbound,即每个Task都有自己的bound。

FreeForm模式下Activity从Recents中启动,调用的接口为AMS.startActivityFromRecents(),其时序跟从任务栏拖动一个应用进入分屏差不多,只是此处Activity在FreeFormStack中启动,其他基本流程基本一致,不再详细分析。

4、窗口resize/拖动

在分屏模式下,拖动中间bar条改变分屏大小,中间bar条是SystemUI添加到系统一个名为“DockedStackDivider”的浮窗,接收触摸事件,根据MotionEvent的Y值动态调用ActivityManagerService.resizeDockedStack()来改变DockedStack的bound大小,完成窗口大小调整。

//FreeForm模式下config发生改变,会触发onConfigurationChanged,调用到此处

//onConfigurationChanged函数图  

窗口调整跟拖拽便是在DecorCaptionView中根据触摸事件检测开启的,调用的开启函数便是WindowManagerService.startMovingTask(IWindowwindow, float startX, float startY)。就拿拖拽来说,用户按住FreeFormActivity的窗口边缘然后移动,此拖动过程分为三步:

 step1.DecorCaptionView检测触摸事件,发现是拖拽或窗口调整,那么跨进程调用WindowManagerService.startMovingTask()开启Task移动;

 step2.WMS.startMovingTask()会创建一个TaskPositioner,TaskPositioner会注册一个InputChannel到InputManager中,此后的触摸事件便会发送到TaskPositioner中来;

  step3.TaskPositioner接收move事件,计算WindowRect然后调用AMS.resizeTask(inttaskId, Rect bounds, int resizeMode)来更新Activity窗口bound。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值