前言
Android N引入了分屏的概念,有两个入口:
- 单击多任务按键,通过长按拖动多任务界面中的task到顶部dock区域后释放。
- 进入到某个应用(非Launcher)后,长按多任务键
对应的,另外一个区域的应用,则是通过点击多任务中的图标来启动。
Split Window First
Dock
从拖动入手,可以接受拖动目标的接口为,DropTarget
,对应的,每部手机 or Set会因为设备不同而做不同的配置,对应的dock热区也会不同,以模拟器Nexus 5为例,dock的热区在顶部:
|
|
What is a Dock ?
对于这里的定义,dock为Top。
|
|
而对于Top是如何被作为mDropTarget的,可以参考
|
|
因此,当整个拖拽的动作开始时,会触发onBusEvent(DragStartEvent event)
,从而准备去注册DropTarget
,而这里DockStates的来源是:getDockStatesForCurrentOrientation();
,在这个函数中我们会去判定当前set(手机or平板)的各种状态,从而给出不同位置的Dock。而在这里我们为了简化问题,就用Phone | Portrait为例。
因此:DockRegion.PHONE_PORTRAIT
=TaskStack.DockState.TOP
最后,TOP与Phone Portrait的关系就这样建立起来了。
How to start a split Activity ?
上文分析了对于Dock为什么会出现在Top,以及Top & DropTarget的关系,所以当我们触发了Drag & Drop的动作以后,自然就会发生分屏这样一个行为了。 我们先看一张函数调用图来快速定位:
从函数调用栈上,我们了解到整个touch的派发是从PhoneWindow下来的,这一路是基于Frameworks的事件流,我们来看看RecentsViewTouchHandler.java
中的handleTouchEvent
|
|
搭上EventBus一路向北
我们来到RecentView.java::onBusEvent(final DragEndEvent event)
|
|
ssp
是SystemServicesProxy
的实例,而对应的入参:event.task.key.id
是需要启动的task id,dockState.createMode
则是启动的mode,这个在创建DockState的时候已经确定:
|
|
createMode中的的两个参数,很不幸,都是属于ActivityManager.java
中的hide元素:
|
|
根据字面意义,我们也大概可以猜测到,当value = 0
也即DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
,其实就是把Activity启动时放在上面or左边(上左优先),当value = 1
也即DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT
,其实就是右下优先。
StartTaskInDockedMode
紧接着我们来到真正的执行点,ssp.startTaskInDockedMode
|
|
AMS,稍后开专门的文章来分析。
Split Window Second
主屏应用分屏结束后,多任务列表的界面会存在与从屏中,此时屏幕差不多是这样显示的:
上面的部分我们认为是主屏,也就是之前分析的DockState,TOP状态下的左上优先。底下的部分我们认为是从屏,可以看到它仍旧是处于多任务列表的模式
注意请忽略掉星星的图标,原生的代码中并没有那一块东西,这里是我在调试UI的时候加入的
StartActivity to slave window
这边是单击事件来触发整个启动的flow,而从这个View的结构我们已经知道每一个task其实是一个TaskView
,因此我们直接来到TaskView.java
并注意看它的onClick
函数:
|
|
在这里,我们需要看一下LaunchTaskEvent
的构造参数:
其中:
taskView
:thistask
:mTasktargetTaskBounds
:nulltargetTaskStack
:INVALID_STACK_IDscreenPinningRequested
: screenPinningRequested
再次搭上EventBus一路向北
这次我们来到了RecentsView.java
::onBusEvent
|
|
这里首先去检查了一下event.task的isFreefromTask
,而这个FreeForm其实就是android N上最新加入的隐藏模式,也就是悬浮窗口,当然这里的悬浮窗与windows上的那种还有区别,它并不支持background activity,对于所有的FreeForm activity,他们都是基于一个统一的wallpaper,然后在其之上堆叠显示。
RecentsTransitionHelper.java的神秘面纱
|
|
想想也是很愚蠢的,所以这边最终还是会进入到startTaskActivity的flow,继续trace:
|
|
Go on for waiting AMS:
|
|
最后是走到了startActivityFromRecents
Another way to SplitWindow
关于长按多任务键,从已经启动的某个app中直接进入到
#写在最后
纵观整个flow,去掉system ui特有的属性,对于启动一个主屏的应用:
其中需要设置的是createMode以及Task id。
而对于启动一个从屏的应用,则是