窗口切换逻辑设计中需要解决的问题
Android中的窗口管理系统中,首先在AmS中保存所有与Activity相关的信息,在Activity启动和关闭时,AmS会通知WmS同步Activity窗口的状态,而在WmS中使用WindowState类保存一个窗口的信息,这些窗口信息需要根据Activity的状态而动态改变。在WmS中另外有一个InputManager对象,该对象内部保存了输入消息处理时所需要的窗口信息,借助这些信息,InputDispatcher能够决定输入消息应该对应哪个窗口,WindowState类的信息必须与InputManager内部的窗口信息也保持同步。
切换过程中要解决的问题可归纳为三类,第一类是状态同步问题,即AmS如何把状态传递给WmS,WmS如何保存这些状态,又如何把这些状态传递给InputManager;第二类问题是屏幕绘制问题,即当AmS启动或者关闭一个Activity时,用户一般会看到一个动画,那么WmS如何定义这个动画,并如何在动画绘制前先隐藏目标窗口,直到动画结束后才显示目标窗口,而要实现这种动画的绘制就需要一种特别的变量来保存动画窗口和目标窗口的关系;第三类问题是消息处理问题,即在窗口切换的过程中,是老窗口应该继续捕获用户消息呢还是新窗口?
首先来看状态同步问题
在AmS中使用ActivityRecord类来保存一个Activity相关的信息,ActivityRecord类本身是一个Binder类,名称为IApplicationToken.Stub。每个ActivityRecord都会在WmS中对应一个AppWindowToken类,该类保存了和ActivityRecord相关的所有窗口信息,比如启动窗口、实际窗口、关闭窗口等。当启动一个新的Activity时,AmS中会先创建一个ActivityRecord对象,并请求WmS中也创建一个AppWindowToken对象;当销毁一个Activity时,AmS会请求WmS删除AppWindowToken对象。
AppWindowToken中包含的窗口对象在WmS中的mWindows等列表变量中也都有记录,当新窗口启动时,必须保证这些列表中的对象和AppWindowToken中保存的窗口信息之间的同步。
WmS中有一个InputManager对象mInputManager,该对象中保存了输入消息处理所需的窗口信息,当有新窗口添加或者旧窗口被删除时,该对象中的窗口信息同时需要被更新。
下面再来看绘制问题。当新建一个窗口时,AmS会首先判断该窗口是同一个Task中的Activity还是一个新的Task。如果是同一个Task,则会指定一个Activity切换效果的动画,而如果是一个新的Task,则会指定一个Task切换的动画效果,这些动画效果的实施都是在WmS中完成的。每一个动画实际上都仅仅是一个窗口而已,动画的过程可以简单地理解为对同一个窗口进行不同的变化,并在连续的时间将其显示出来,从而形成动画。
而在动画的过程中,如果目标窗口也已经创建好,则在动画结束之前不能显示目标窗口,只有当动画结束后才能显示目标窗口;而如果在动画结束后目标窗口还没有被创建,则启动窗口不消失,直到目标窗口被创建好才消失。这个逻辑的具体实现是在performLayoutAndPlaceSurfaceLocked()函数中完成的,当然该函数可能仅仅是个包装,内部又会调用performLayoutAndPlaceSurfaceLockedInner()函数。为了叙述的方便,本节以后将这两个函数统称为traversal,因为它们的功能就是根据所有窗口的状态参数,调整其在屏幕上的显示效果,有点类似于ViewRoot类中的performTravasals()函数。
最后再来看消息处理问题。当要添加一个新窗口时,如果该窗口需要启动动画,那么在动画结束前,应该是哪个窗口获得输入消息呢?在WmS中提供了诸如startAppFreezingScreen()、topAppFreezingScreen()、resumeKeyDispatching()、pauseKeyDispatching()、setEventDispatch(true/false)这样的接口,用于暂停、恢复消息处理,这些函数将影响在动画过程前后的输入消息处理。那么这些API接口具体是被如何使用的?