【Android14 ShellTransitions】(四)Transition收集动画参与者

这一节的内容在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那样。

相关推荐
小李飞飞砖3 分钟前
Android 插件化实现原理详解
android
m0_5973453111 分钟前
【Android】安卓四大组件之广播接收器(Broadcast Receiver):从基础到进阶
android·java·boradcast·安卓四大组件
whysqwhw34 分钟前
OkHttp PublicSuffix包的后缀列表处理
android
yeziyfx1 小时前
kotlin中集合的用法
android·开发语言·kotlin
EngZegNgi3 小时前
安卓应用启动崩溃的问题排查记录
android·crash·启动崩溃
火柴就是我3 小时前
每日见闻之Container Decoration
android·flutter
天枢破军3 小时前
【AOSP】解决repo拉取提示无法连接android.googlesource.com
android
whysqwhw4 小时前
OkHttp之AndroidPlatform类分析
android
XiaolongTu4 小时前
Kotlin Flow详述:从一个“卡顿”问题到线程切换的本质
android·面试
Kapaseker4 小时前
全网最详细的Compose Stable讲解,你一定要看
android