浅谈Android多屏幕的事
一部手机可以同时看片、聊天,还可以腾出一支手来撸!这么吊的功能(非N版本,非第三方也能实现,你不知道吧)摆在你面前,你不享用?不关注它是怎样实现的?你来,我就满足你的欲望!
一部手机可以同时看片、聊天,还可以腾出一支手来撸==!就像这样:
是时候告别来回切换应用屏幕的酸爽了,还可以在分屏模式下两Activity间直接拖放数据!
好高大上的样子!这是怎么实现的?别急,我们一一道来:
kitkat(4.4)版本对多任务分屏的实现
由于相关的代码和功能被封装及隐藏起来,所以我们从dumpsys activity命令作手。
来,直接上一条命令:
am stack boxes
NetEasedeMac-Pro-8:~ netease$ adb shell am stack boxes
Box id=0 weight=0.0 vertical=false bounds=[0,75][1080,1776]
Stack=
Stack id=0 bounds=[0,75][1080,1776]
taskId=1: com.android.launcher/com.android.launcher2.Launcher
taskId=4: com.android.systemui/com.android.systemui.recent.RecentsActivity
Box id=1 weight=0.0 vertical=false bounds=[0,75][1080,1776]
Stack=
Stack id=1 bounds=[0,75][1080,1776]
taskId=2: com.android.contacts/com.android.contacts.activities.PeopleActivity
taskId=3: com.android.email/com.android.email.activity.Welcome
以上命令大致可以看出一些简单明显的东西,最高级为两个Box,每个Box有一个Stack,每个Stack下有n个taskId等等。既然提到am,那我们就去看一看Android源码Am这个类:com.android.commands.am.Am。在里面可以看到一堆的am命令,这些命令的执行在onRun()函数里,我们先看runStack()->runStackBoxes(),一直跟踪下去,会涉及到ActivityManagerService.getStackBoxes()->WindowManagerService.getStackBoxInfos()->DisplayContent.getStackBoxInfos()直接遍历DisplayContent里的mStackBoxes。
用简单的类图来表示其数据结构关系:
StackBox相关简单类图:
TaskStack相关的简单类图:
有这里Task的概念是什么?
官方定义:“A task (from the activity that started it to the next task activity)defines an atomic group of activities that the user can move to.”。
简单来讲:Task是为了完成一个功能的一系列相关的有序Activity集合,可以理解为用户与App之间对于特定功能的一次会话。一个Task中的Activity可以来自不同的App,比如在邮件App中需要看图片附件,然后会开imageview的Activity来显示它。
以上只是在WMS里分析了StackBox、TaskStack相关的简单数据结构,我们知道对于一个完整的窗口流程还主要涉及到AMS、SF联合管理及渲染显示。那么在AMS里是怎样进行管理的呢?不急,我们看第二个灵魂级的am命令:
灵魂级的am命令:am stack create
am stack create <TASK_ID> <RELATIVE_STACK_BOX_ID> <POSITION> <WEIGHT>
注解如下:
am stack create: create a new stack relative to an existing one.
<TASK_ID>: the task to populate the new stack with. Must exist.
<RELATIVE_STACK_BOX_ID>: existing stack box's id.
<POSITION>: 0: before <RELATIVE_STACK_BOX_ID>
1: after <RELATIVE_STACK_BOX_ID>
2: to left of <RELATIVE_STACK_BOX_ID>
3: to right of <RELATIVE_STACK_BOX_ID>
4: above <RELATIVE_STACK_BOX_ID>
5: below <RELATIVE_STACK_BOX_ID>
<WEIGHT>: float between 0.2 and 0.8 inclusive.\n" +
试一下:
NetEasedeMac-Pro-8:~ netease$ adb shell am stack create 3 1 4 0.7
createStack returned new stackId=2
直接上效果图:
这里申明一下:由于我的模拟器有问题,上图是借用http://androidinternalsblog.blogspot.com/2014/03/split-screens-in-android-exist.html 里的,不过效果一样的。
这就是android分屏的鼻祖,这时候的stack boxes输出如下:
NetEasedeMac-Pro-8:~ netease$ adb shell am stack boxes
Box id=1 weight=0.5 vertical=true bounds=[0,75][1080,1776]
First child=
Box id=2 weight=0.0 vertical=false bounds=[0,75][1080,925]
Stack=
Stack id=2 bounds=[0,75][1080,925]
taskId=3: com.android.email/com.android.email.activity.Welcome
Second child=
Box id=3 weight=0.0 vertical=false bounds=[0,925][1080,1776]
Stack=
Stack id=1 bounds=[0,925][1080,1776]
taskId=2: com.android.contacts/com.android.contacts.activities.PeopleActivity
Box id=0 weight=0.0 vertical=false bounds=[0,75][1080,1776]
Stack=
Stack id=0 bounds=[0,75][1080,1776]
taskId=1: com.android.launcher/com.android.launcher2.Launcher
可以看到之前的Box_id1一分为二:Box_id2和Box_id3(充分说明StackBox的数据结构为树),另外stack_id3从原Box_id1(也可以理解成现在的Box_id2)的移到了Box_id3。
看看am stack create是怎么实现的吧,回到Am里的runStackCreate()->AMS.createStack()
public int createStack(int taskId, int relativeStackBoxId, int position, float weight) {
enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
"createStack()");
if (DEBUG_STACK) Slog.d(TAG, "createStack: taskId=" + taskId + " relStackBoxId=" +
relativeStackBoxId + " position=" + position + " weight=" + weight);
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
int stackId = mStackSupervisor.createStack();
mWindowManager.createStack(stackId, relativeStackBoxId, position, weight);
if (taskId > 0) {
moveTaskToStack(taskId, stackId, true);
}
return stackId;