【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那样。

相关推荐
无极程序员1 小时前
PHP常量
android·ide·android studio
萌面小侠Plus2 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机
慢慢成长的码农2 小时前
Android Profiler 内存分析
android
大风起兮云飞扬丶2 小时前
Android——多线程、线程通信、handler机制
android
L72562 小时前
Android的Handler
android
清风徐来辽2 小时前
Android HandlerThread 基础
android
HerayChen3 小时前
HbuildderX运行到手机或模拟器的Android App基座识别不到设备 mac
android·macos·智能手机
顾北川_野3 小时前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
hairenjing11233 小时前
在 Android 手机上从SD 卡恢复数据的 6 个有效应用程序
android·人工智能·windows·macos·智能手机
小黄人软件4 小时前
android浏览器源码 可输入地址或关键词搜索 android studio 2024 可开发可改地址
android·ide·android studio