说来惭愧,AndroidU都已经开发这么久了,但是我还没有整理过ShellTransitions相关的知识。我本来希望能够系统的写一篇关于ShellTransitions的笔记出来,但是发现一来这是一个比较庞大的模块,二来我个人能力有限,对ShellTransitions目前掌握的并不透彻,只是朦胧上大体有一个认知。如果把ShellTransitions的方方面面都了解清楚再去写这么一篇笔记,google说不定已经不用ShellTransitions了,就像我刚刚弄清楚AppTransition和AppTransitionController这块的逻辑,AndroidU就把动画逻辑变成ShellTransitions了一样,有点不讲武德了。所以趁ShellTransitions还健在,我觉得还是趁热打铁吧,哪天看了ShellTransitions的代码有点感悟了就去做相关记录,这样做的缺点就是知识点比较零散,写的会比较随意,并且也可能有理解的不到位的地方,但是希望可以靠后面的持续更新来慢慢改善这些不足,总比ShellTransitions坟头草都三米高了才想起来要总结要好。
废话就到这里,现在开始吧。
1 ShellTransition相关类
网上关于ShellTransition的介绍还是比较少的,这个时候想要立刻能够对ShellTransition有一个大概认知,我一般会看注释,所以这里也是先看ShellTransition相关的类的注释。
1.1 TransitionController
首先是TransitionController,它有点像之前的AppTransitionController,是WMCore这边的过渡动画的控制器调度器之类的,控制过渡动画的生命周期。
另外一提,这里有一个WMCore和WMShell的新概念,似乎是和这些的类的包名有关,比如TransitionController这个类,它的目录为:
frameworks\base\services\core\java\com\android\server\wm\TransitionController.java
包名为:com.android.server.wm
它被归纳为WMCore里的。
而下面的Transitions类,它的目录为:
frameworks\base\libs\WindowManager\Shell\src\com\android\wm\shell\transition\Transitions.java
包名为:com.android.wm.shell.transition
它被归纳为WMShell里的。
好了,继续看TransitionController的注释:
处理记录(收集)和同步过渡动画的所有方面。 这仅涉及WM更改。 实际的动画由Player处理。
目前,一次只能有 1 个过渡动画成为主要“收集器”。 这是因为 WM 更改仍然以“全局”方式执行。 然而,收集实际上可以分为两个阶段:
1. 实际进行WM更改并记录参与的容器。
2. 等待参与容器准备就绪(例如重绘内容)。
因为 (2) 花费了大部分时间并且不会改变 WM,所以我们实际上可以在阶段 (2) 中同时进行多个Transition,同时在阶段 (1) 中进行一个Transition。 我们将这种安排称为“并行”收集,尽管实际上仍然只有 1 个Transition能够获得参与者。
当“主收集器”完成“设置”(第 1 阶段)并等待时,会发生并行收集。 此时,另一个过渡动画可以开始收集。 发生这种情况时,第一个Transition将移至“等待”列表,新过渡动画将成为“主收集器”。 如果在任何时候,“主收集器”在其中一个等待过渡动画之前开始播放,则第一个等待过渡动画将变回“主收集器”。 这维持了 WM 其余部分当前所期望的“全局”抽象。
当一个过渡动画移动到播放时,我们会根据所有其他播放过渡动画进行检查。 如果它不与它们重叠,它也可以并行动画。 在这种情况下,它将被分配一个新的“轨道”。 “轨道”是一种与Player沟通哪些过渡动画需要彼此串行播放的方式。 因此,如果我们发现一个过渡动画与一个轨道中的其他过渡动画重叠,则该过渡动画将被分配给该轨道。 但是,如果过渡动画与 大于1个轨道中的过渡动画重叠,我们实际上会将其标记为 SYNC,这意味着在所有先前的过渡动画完成之前它无法实际播放。 这是一种笨拙的做法,因为这是一种后备情况,支持更高级的东西会变得不必要的复杂。
1.2 Transitions
播放过渡动画。 在这个Players中,每个过渡动画都有一个生命周期。
-
当直接启动或请求过渡动画时,将其添加到“PENDING ”状态。
-
一旦 WMCore 应用过渡动画并发出通知,过渡动画就会转至“READY ”状态。
-
当过渡动画开始动画时,它会转至“ACTIVE ”状态。
基本上:--start--> PENDING --onTransitionReady--> READY --play--> ACTIVE --finish --merge--> MERGED
READY 及以后的生命周期按“轨道”进行管理。 在一个轨道中,所有动画都按描述的方式串行排列; 但是,多个轨道可以同时播放。 这意味着,在一个轨道内,一次只能有一个过渡动画处于动画状态(“ACTIVE ”)。
当一个过渡动画在一个轨道中进行动画时,分派到该轨道的过渡动画将以“READY ”状态排队等待轮到。 同时,每当过渡动画到达“READY ”队列的头部时,它将尝试与“ACTIVE ”状态的过渡动画合并。 如果合并成功,它将被移动到“ACTIVE ”状态的过渡动画的“合并”列表,然后下一个“READY ”状态的过渡动画可以尝试合并。 一旦“ACTIVE ”状态的过渡动画完成,就可以播放下一个“READY ”状态的过渡动画。
轨道分配预计由 WMCore 提供,这通常会尝试保持相同的分配。 但是,如果 WMCore 确定过渡动画与大于1个活动轨道冲突,则会将其标记为 SYNC。 这意味着在 SYNC 过渡动画可以播放之前,必须刷新所有当前活动的轨道。
1.3 过渡动画
由于WMCore侧的TransitionController和WMShell侧的Transitions职责有所不同:
1、WMCore侧,对应系统进程,管控过渡动画的整个生命周期。
2、WMShell侧,目前对应SystemUI进程,主要是处理过渡动画的播放。
因为过渡动画从创建到结束整体下来是一套复杂的流程,期间WMCore和WMShell会有多次通信,那么他们各自都得有一个类来代表过渡动画,即WMCore侧的Transition和WMShell侧的ActiveTransition。
1.3.1 Transition
Transition是过渡动画在WMCore侧的代表,其内部定义了Transition可能处于的几个状态值,其成员变量mState保存了Transition当前所处的状态,默认是STATE_PENDING。
1.3.2 ActiveTransition类
ActiveTransition是过渡动画在WMShell侧的代表,ActiveTransition类定义在Transitions内部,通过其IBinder类型的成员变量mToken与WMCore侧的Transition一一对应。
2 过渡动画整体流程
看了注释其实还是挺懵b的,这里再用我个人的理解介绍一下过渡动画的状态流转。
2.1 Transition状态流转
首先Transition是有一个状态集的,刚刚介绍Transition类的时候也看到了,定义在Transition类内部:
1)、STATE_PENDING,当Transition刚被创建的时候就是这个状态。
典型的是在各种需要动画的流程下调用TransitionController.createTransition来创建一个Transition对象。
2)、STATE_COLLECTING,当Transition开始收集动画参与者的时候,就会被置为这个状态。
一般创建完Transition后就会调用TransitionController.moveToCollecting,最终在Transition.startCollecting中,Transition从STATE_PENDING状态切换到STATE_COLLECTING状态。
3)、STATE_STARTED,Transition已经被正式启动,它仍在收集中,但一旦所有参与者准备好进行动画(完成绘制),它将停止。结合STATE_COLLECTING,这里的意思应该就是说,如果Transition没有被start,那么它将一直处于收集参与者的状态,即使所有参与者都已经完成绘制可以开始动画了,但是因为当前Transition没有被启动,所以也无法进行下一步。
首先WMCore侧会在合适的时机调用TransitionController.requestStartTransition切换到WMShell,当WMShell侧也启动了一个ActiveTransition后,会调用WindowOrganizer.startTransition切换回WMCore侧,最终调用Transition.start,在Transition.start中Transition从STATE_COLLECTING状态切换到STATE_STARTED状态。
4)、STATE_PLAYING,Transition正在播放动画,并且不能再继续收集了,也不能被改变。也就是说此时谁谁谁要播放动画已经确定了,并且这些参与者已经开始播放动画了,所以就不能再去收集新的参与者了,而且也不能对当前Transition进行修改了。
当所有动画参与者都已经绘制完成,可以开始动画了,那么WMCore侧就会走到Transition.onTransactionReady,走到这一步就认为Transition已经就绪了,这里Transition将会从STATE_STARTED状态切换到STATE_PLAYING状态,然后调用ITransitionPlayer.onTransitionReady切换到WMShell。
5)、STATE_FINISHED,Transition已经成功播放完了动画。
WMShell侧主要负责播放动画,当WMShell侧播放动画完成后,会调用WindowOrganizer.finishTransition切换回WMCore侧,最终在Transition.finishTransition中,Transition从STATE_PLAYING状态切换到了STATE_FINISHED状态。
6)、STATE_ABORTED,Transition正在被中止或者已经中止,不会播放任何动画,也不会将任何内容发给player。
当调用Transition.abort方法,Transition的状态会被置为STATE_ABORTED,这个动作可能发生在任意时刻。
此处也许需要一个流程图:
2.2 ActiveTransition状态流转
如之前看到的,ActiveTransition的状态流转定义在Transitions的注释中:
看到其实代码中是没有定义注释中说的那些READY、MERGED的状态的。
刚开始写这篇文章的时候其实还没有仔细把过渡动画的整体流程过一遍,经过评论区(这篇文章csdn也有发)的指正,发现自己对注释中提到的各个状态值理解的并不准确,以现在的知识储备重新去阅读注释,有了一些新的理解,这里重新介绍如下:
之前我是把Transition的状态和ActiveTransition的状态混着说了,这里的READY和MERGED等状态描述的是WMShell侧ActiveTransition的状态,并非是WMCore侧Transition的状态,最直接的证据就是这个注释写在Transitions中而不是TransitionController中,当然接下来也会根据WMShell侧的代码来介绍一下。
再者虽然WMShell侧没有像WMCore侧那样,将READY和MERGED等状态都定义为一个个的状态值,但是这里也的确是有这个状态的概念的,这里再根据代码理解一下这个注释提到的ActiveTransition的状态流转。
另外上面的注释:
小写代表一个瞬间的动作/操作,这个操作改变了过渡动画的状态。
大写代表代表了过渡动画在某一段时间内所处的状态,与瞬间的操作相对。
1)、start,这对应一个启动过渡动画的操作,这个操作可以由WMCore侧在TransitionController.requestStartTransition发起,也可以由WMShell侧直接通过Transitions.startTransition发起。
这一步的最终结果是在WMShell侧创建了一个ActiveTransition对象,并且保存在了Transitions的mPendingTransitions成员变量中,该成员变量保存的是已经被启动但是还没有就绪的过渡动画,因此可以认为此时ActiveTransition进入了PENDING状态。
2)、onTransitionReady,这对应了过渡动画就绪的这个时间点,由WMCore侧在Transition.onTransactionReady中发起。当WMCore侧的Transition就绪,就会来到Transition.onTransactionReady方法,然后通过ITransitionPlayer.onTransitionReady来通知WMShell过渡动画已经就绪可以行下一步了。
这一步的最终结果是,在Transitions.dispatchReady中创建了一个Track对象, 并且把当前WMCore侧就绪的Transition对应的那个ActiveTransition对象保存到了Track.mReadyTransitions中,该成员变量保存的是已经就绪正在排队等待播放的过渡动画,因此可以认为此时ActiveTransition从PENDING状态切换到了READY状态。
3)、接下来根据当前READY的ActiveTransition之前是否已经有正在播放的ActiveTransition,分为两种情况,如注释这里的表示:
3.1)、play,这对应之前没有正在播放的ActiveTransition(或者说没有处于ACTIVE状态的ActiveTransition)的情况,那么当前就绪的ActiveTransition就要开始播放动画了。在上一步,在Transitions.dispatchReady中ActiveTransition从PENDING状态切换到了READY状态,然后Transitions.dispatchReady紧接着调用了Transitions.processReadyQueue。
这一步的最终结果是在Transitions.processReadyQueue中,就绪的那个ActiveTransition从Track的mReadyTransitions移动到了Track的mActiveTransition,因此可以认为此时ActiveTransition从READY切换到了ACTIVE状态。接下来就是调用TransitionHandler.startAnimation来播放动画了。
3.2)、merge,这对应之前有正在播放的Activetransition的情况,那么这里会调用TransitionHandler.mergeAnimation。
这一步的最终结果是READY状态的过渡动画会和ACTIVE状态的过渡动画进行合并,即当前就绪的ActiveTransition从READY切换到了MERGED状态。
4)、最后不管是ACTIVE状态的过渡动画,还是MERGED状态的过渡动画,等到它们的动画播放完成后,都会回调Transition.onFinish,最终调用WindowOrganizer.finishTransition切换回WMCore,走最终的过渡动画结束的finishTransition流程。
直到此时我才知道这里的“^”是一个箭头,代表的是从过渡动画从“MERGED”节点转到下一步的“finish”节点......有点抽象了。
流程图:
2.3 总流程图
最后是WMCore和WMShell交互的流程图:
接下来我们以在Launcher界面点击App图标启动某个App为例,来分析一般过程,但是无法面面俱到,一些细节希望靠后续的系列文章可以补充。