这一节的内容在WMCore中,现在Transition已经走到COLLECTING状态了,并且可以收集动画参与者了。
那么Transition是在什么时候去收集动画参与者?回到我们最初的ActivityStarter.startActivityUnchecked:
在调用了TransitionController.createAndStartCollecting后,紧接着就是调用了TransitionController.collect。
这里看到TransitionController里面首先是判断了一下成员变量mCollectingTransition是否为空,如果不为空,才能将传参WindowContainer收集到该Transition中。
接下来我们重点分析Transition.collect,这里感觉不按照代码顺序分析似乎更有条理一点。
1 Transition.collect第1部分
1)、如果当前Transition的mState小于STATE_COLLECTING,那么就会异常。
2)、如果当前Transition的mState不是STATE_COLLECTING或者STATE_STARTED,那么就会返回。
这里说明了,只有处于STATE_COLLECTING或者STATE_STARTED的Transition才能收集动画参与者,太早或者太晚都不行。
2 Transition.collect第2部分
首先看这里needSync的逻辑。
如果,当前正在被收集的WindowContainer不是一个WallpaperWindowToken,
或者,当前参与者中不包含该WindowContainer所在的DisplayContent,
并且,该WindowContainer没有被瞬态隐藏(被Recents遮挡),
那么needSync就为true。
needSync为true,那么继续调用BLASTSyncEngine.addToSycnSet方法:
很简单,根据传入的Transition的ID找到对应的SyncGroup对象,然后继续调用SyncGroup.addToSync方法:
该方法内容主要是:
1)、如果当前WindowContainer通过getSyncGroup拿到的SyncGroup为空,说明当前WindowContainer还没有绑定一个SyncGroup,那么就将该WindowContainer添加到SyncGroup.mRootMembers中,并且通过WindowContainer.setSyncGroup方法,将WindowContainer的成员变量mSyncGroup设置为当前SyncGroup,完成绑定。
2)、调用WindowContainer.prepareSync方法。
关于这个prepareSync的逻辑,同样在我们之前分析BLASTSyncEngine的那篇文章中也比较系统的介绍过,因此这里简单再回顾一下。
首先是WindowContainer.prepare方法:
比较简单,调用子WindowContainer的prepareSync方法,然后将自身的mSyncState设置为SYNC_STATE_READY。
mSyncState有三种状态:
- SYNC_STATE_NONE,没有参与到一次同步(或Transition)中。
- SYNC_STATE_WAITING_FOR_DRAW,等待自身绘制完成。
- SYNC_STATE_READY,当前WindowContainer已经就绪,但是其子容器可能没有就绪。
一般来说,动画都伴随着界面的改变,比如我们从Launcher启动Contacts,Contacts是一个从无到有的过程,那么动画就需要等待Contacts的窗口绘制完成后才能进行。
因此再看重写了prepareSync方法的WindowState.prepareSync方法的内容:
重点其实就是这一句,将WindowState的mSyncState置为SYNC_STATE_WAITING_FOR_DRAW。
即对于非WindowState的WindowContainer,比如Task,ActivityRecord,它们在界面发生改变的时候是不需要重绘的,因此它们在prepareSync的时候就可以将mState设置为SYNC_STATE_READY。同理,判断一个Task或者一个ActivityRecord是否就绪,看的主要是它里面的WindowState是否就绪了。
而对于WindowState,在界面发生改变的时候,由于要重绘,并且重绘需要时间,因此WindowState在被添加到动画参与者集合的时候,会先被设置为SYNC_STATE_WAITING_FOR_DRAW。等到绘制完成,为该WindowState调用finishDrawing的时候,它的mSyncState会被设置为SYNC_STATE_READY,表示该窗口已经就绪,可以进行动画。
3 Transition.collect第3部分
接着看Transition的成员变量mParticipants:
是一个ArraySet类型的成员变量,看方法最后,是将参与当前Transition的WindowContainer添加进来了,那它的作用和SyncGroup的mRootMembers类似。
从第一部分关于needSync的分析看到,向SyncGroup.mRootMembers添加参与者的条件要比向Transition.mParticipants的条件苛刻一点,因为这个区别,这两个参与者集合中的内容可能不是完全相同的。
4 Transition.collect第4部分
最后我们再来看一下关于ChangeInfo的这部分。
4.1 例子1 ------ 新启动ActivityRecord
还以我们之前分析的从Launcher启动Contacts的流程来,那么在这个时间点,只创建了一个ActivityRecord对象,它的Task还没创建,那么通过Transition.getAnimatableParent获取的WindowContainer自然也就是null了,因此标黄的第一段逻辑就不走了,直接看第二段逻辑。
首先看下Transition.mChanges的定义:
一个包含了WindowContainer和ChangeInfo键值对的map。
这是我们第一次尝试将当前WindowContainer添加到动画参与者中,因此从Transition.mChanges中肯定是没有当前WindowContainer的,因此这里就会根据该WindowContainer创建一个ChangeInfo对象,并且将这两个对象组成的键值对保存到Transition.mChanges中。
ChangeInfo的构造方法为:
用来保存改变开始前的WindowContainer的一些信息,大概是这样的用法:
1)、在收集阶段,将基于WindowContainer的当前信息创建一个ChangeInfo,ChangeInfo里保存了我们对WindowContainer感兴趣的特定信息。
2)、发生一些改变。
3)、改变完成后,具体是在Transition.onTransactionReady的时候,拿第一步创建ChangeInfo和现在这个时候的WindowContainer的信息进行对比,从而得知当前WindowContainer在这个过程中发生了哪些变化。
4.2 例子2 ------ App切换
在第一个例子中,我们看到由于此时是ActivityRecord刚刚创建的时候,因此ActivityRecord的父容器为空,所以这里仅仅为正在收集的这个ActivityRecord创建了一个ChangeInfo的对象。然而更普遍的情况可能是,不仅为当前正在收集的这个WindowContainer创建了ChangeInfo,还可能会创建更多ChangeInfo对象, 即这段逻辑:
Transition.getAnimatableParent的定义为:
如果发生了App的切换,比如从Message界面点击Home回到Launcher,那么就会把Message和Launcher的Task收集起来,走到这里时,传参是Task类型的WindowContainer:
1)、首先Task通过getParent获取到父WindowContainer,即TaskDisplayArea。
2)、TaskDisplayArea复写了WindowContainer.canCreateRemoteAnimationTarget方法:
返回的是true,那么通过Transition.getAnimatableParent返回了TaskDisplayArea。
3)、最终会为TaskDisplayArea创建一个ChangeInfo对象,添加到Transition.mChanges中。
同理为TaskDisplayArea调用Transition.getAnimatableParent方法,获取到的则是DisplayContent,也会为DisplayContent创建一个ChangeInfo对象添加到Transition.mChanges中。
那么除了为Task创建ChangeInfo之外,这里还会为TaskDisplayArea以及DisplayContent创建ChangeInfo,并且加入到Transition.mChanges中。
最后再来对比一下SyncGroup.mRootMembers、Transition.mParticipants以及Transition.mChangs:
1)、Transition.mParticipants,保存的就是正在收集的那个WindowContainer。
2)、SyncGroup.mRootMembers,由于一些条件限制,保存的WindowContainer比Transition.mParticipants要少一点。
3)、Transition.mChangs,不仅保存了正在收集的那个WindowContainer对应的ChangeInfo对象,还可能保存了它的父WindowContainer对应的ChangeInfo对象。
5 其它collect的情况
看下Transition.collect都在哪些地方调用了:
并且它也会被TransitionController.collect调用,而TransitionController.collect被调用的地方同样也很多:
场景太多就不一一分析了,其实万变不离其宗,需要在什么节点把WindowContainer收集进来再去调用Transition.collect就行了,就像之前把ActivityRecord添加到openingApps和closingApps那样。