一、概述
Android从7.0开始支持多窗口,官方终于支持这一功能了。其实很多ODM厂商早已实现该功能,实现方式各种各样,最通用的方案是多Stack方案,比较痛苦的是每次版本升级都要花很大力气进行适配和解各种乱七八糟的bug,现在终于一统江湖了,并且第三方应用开始对多窗口进行适配兼容。
Android原生的多窗口功能比较强大,支持四种模式:全屏、分屏、画中画、FreeForm模式。目前都是可以直接或间接开启的,还有一些bug。多窗口主要涉及ActivityManagerService、WindowManagerService、Input三个模块,前两个模块本身已经很复杂了,现在支持四种模式多窗口,整个复杂度上升了几倍(ActivityManagerService简称AMS,WindowManagerService简称WMS)。
本文所有分析均基于Android 7.1。
二、原理框架
Android原生多窗口也是多Stack方案,即存在多个ActivityStack。ActivityStack是一个抽象的栈,每个栈都有自己的屏幕区域bound和id,Activity是以Task方式组织并放在某一个Stack中的。比如,桌面Launcher、任务栏Recents属于id=HOME_STACK的栈中。例如,上下分屏模式,此时就有上下两个栈,屏幕被分割成上下两部分bound。
AMS和WMS中对Stack分别用ActivityStack和TaskTack描述,通过StackId来映射。对Task用分别用TaskRecord、Task描述,通过TaskId来映射。AMS与WMS中的Stack、Task堆栈顺序会在Activity启动之前进行同步。每个窗口都有一个Z-Order值,保存在WindowState.mLayer中,值越大窗口越高。窗口Z-Order值的计算是又是根据Stack、Task堆栈顺序来的,所以AMS中的Stack、Task堆栈最终决定窗口的Z-Order(浮窗例外)。
AMS中Stack、Task栈保存的位置。Stack栈保存在ActivityStackSupervisor.ActivityDisplay的mStacks中。ActivityStack.mStacks变量是对ActivityStackSupervisor.ActivityDisplay的mStacks的引用,所以可以看到对Stack栈的调整直接在ActivityStack函数中完成,比如moveToFront()。Task栈保存在ActivityStack的mTaskHistory中。
WMS中Stack、Task栈保存的位置。Stack栈保存在DisplayContent.mStacks中。Task栈保存在TaskStack.mTasks中。
每个Activity显示在所属ActivityStack的bound区域内,多个Activity显示在各自ActivityStack的bound区域内,这样就可以实现多窗口。但是FreeForm模式下,Activity的bound由所属Task决定,而非Stack。多窗口不仅仅是控制Activity放入不同ActivityStack中,同时还要改变Activity的生命周期,即Focus Activity是resume状态,其他可见Activity是Pause状态,并不会进入Stop状态。