【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 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android
带电的小王3 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡3 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道4 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库5 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道5 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe5 小时前
Android Hook - 动态加载so库
android
居居飒6 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He9 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗9 小时前
Android笔试面试题AI答之Android基础(1)
android