【Android14 ShellTransitions】(五)启动Transition

这一节的内容涉及WMCore以及WMShell,主要是启动Transition。

回到ActivityStarter.startActivityUnchecked方法:

看下最后启动Transition的部分,在ActivityStarter.handleStartResult中:

只关注我们要关注的部分。

首先是如果这里的局部变量isStarted为true,就说明这个ActivityRecord是新建的,因此调用TransitionController.collectExistenceChange方法:

将从正在收集的这个Transition的mChanges中找到这个新建的ActivityRecord的ChangeInfo对象,将其成员变量mExistenceChanged置为true,表示这个WindowContainer在本次改变前后的存在性发生了变化,即从无到有(OPEN),或者从有到无(CLOSE)。

再回到ActivityStarter.handleStartResult,根据我们的分析流程,这里传入的newTransition不为空,所以所以这里继续调用的是TransitionController.requestStartTransition方法。

1 WMCore部分 ------ TransitionController.requestStartTransition

从注释可知,该方法用来向Player(WMShell)端创建一个Transition,但是不要启动。

该方法的内容主要为:

1)、创建一个TransitionRequestInfo对象:

TransitionRequestInfo是继承了Parcelable的类,用来将WMCore端的一些信息进行封装,发送给WMShell端。

比如这里的triggerTask,如果不为空,就包含了那个生命周期变化(start或者finish)导致本次Transition发生的那个Activity对应的Task的信息。

2)、调用成员变量mTransitionPlayer的requestStartTransition方法,先看这个成员变量mTransitionPlayer:

类型为ITransitionPlayer,它的定义为:

大概翻译为:

该接口由WMShell实现,用于启动和播放过渡动画。和IWindowOrganizerController一起使用的流程如下:

  1. WMCore启动一个Activity并调用requestStartTransition方法。
  2. 这个TransitionPlayer的实现执行相应操作,然后调用IWindowOrganizerController的startTransition方法,告诉WMCore正式开始(在此之前,WMCore将收集Transition中的所有更改,但不会认为Transition已准备好进行动画)。
  3. 一旦Transition中所有收集的更改都完成绘制,WMCore将调用onTransitionReady方法,在这里委托实际的动画。
  4. 一旦这个TransitionPlayer的实现完成动画,它通过IWindowOrganizerController的finishTransition方法通知WMCore。此时,ITransitionPlayer的责任结束。

ITransitionPlayer接口中包含两个方法:

  • onTransitionReady: 当Transition的所有参与者准备好进行动画时调用。作为对startTransition方法的响应。
  • requestStartTransition: 当WMCore中的某些内容需要播放Transition时调用,例如在新Task中启动Activity时。

该注释已经解释的很清楚了,现在我们已经在WMCore端封装好需要传递的信息了,下一步就是调用ITransitionPlayer.requestStartTransition将这些信息发送到WMShell端,来通知那边创建一个Transition。

2 WMShell部分 ------ Transitions.requestStartTransition

从上面ITransitionPlayer的注释我们知道了ITransitionPlayer的实现端在WMShell,具体则是定义在Transitions中的TransitionPlayerImpl类:

它实现了ITransitionPlayer中定义的两个方法接口,我们先分析requestStartTransition,后面等遇到的时候再分析onTransitionReady。

Transitions.requestStartTransition的内容为:

1)、创建一个ActiveTransition对象:

ActiveTransition中有一个IBinder类型的mToken成员,用来保存从WMCore中传来的Transition的token,那么可以认为WMCore端的Transition和WMShell端的ActiveTransition是一个一一对应的关系,这样也有助于理解TransitionController.requestStartTransition的注释:

"Asks the transition player (shell) to start a created but not yet started transition."

这里要player创建的transition并非Transition,而是ActiveTransition。

ActiveTransition中的其它成员变量也非常重要,不过现在我不打算全部说明,用到的时候再说吧。

2)、调用Transitions.mHandlers中的每一个TransitionHandler的handleRequest方法,来为这个刚刚创建的ActiveTransition找到一个后续走到Transitions.onTransitionReady时,执行动画的Transitions.TransitionHandler对象,并且添加到Transitions.mPendingTransitions中。后续走到Transitions.onTransitionReady的时候,再从Transitions.mPendingTransitions中拿需要执行动画的TransitionHandler对象。

Transitions的mHandlers成员变量是一个TransitionHandler类型的队列:

TransitionHandler表示可以处理一组过渡动画的接口:

该接口定义了以下方法:

  • startAnimation: 开始一个过渡动画。如果handleRequest方法返回非空值,则始终调用此方法来处理特定Transition。否则,仅当之前没有其他handler处理Transition时才调用此方法。
  • handleRequest: 可能处理startTransition请求。
  • 等等...
    开机SystemUI进程起来后,就会向Transitions.mHandlers中添加多个TransitionController的子类:

从名字上也能看出这些TransitionController的子类对应的不同类型的Transition的动画。

3)、当创建了ActiveTransition后,下一步就是决定哪一个TransitionHandler来处理当前Transition,具体说来则是遍历Transitions.mHandlers,按照顺序对每一个TransitionHandler调用handleRequest方法,看哪一个子类可以处理这个Transition,比如我这里打印的log:

如果找到了这么一个TransitionHandler对象,那么就把它保存在ActiveTransition.mHandler中,后续onTransitionReady流程会用。

4)、调用WIndowOrganizer.startTransition来启动一个已经创建的Transition,这最终会调用到WindowOrganizerController.startTransition,这就又回到WMCore的部分了,下一节再说。

5)、将WMCore传过来的Transition的token赋值给ActiveTransition.mToken。

6)、将这个创建的ActiveTransition对象添加到成员变量mPendingTransitions中:

成员变量mPendingTransitions是一个ActiveTransition的队列,用来保存那些已经启动,但是还没有就绪的Transition。

后续Transition就绪的时候,即onTransitionReady流程,就会从mPendingTransitions中拿ActiveTransition对象来执行动画。

3 WMCore部分 ------ Transition.start

直接看WindowOrganizerController.startTransition:

从WMCore到WMShell再到WMCore,我们始终传递着IBinder类型的Transition对应的token,因此这里的Transition是不为null的。

因此只需要关注最后调用的Transition.start。

正式启动Transition,再次之前可以收集参与者,但是直到启动后Transition才会被视为就绪,即使所有的参与者都已经绘制完成。

1)、将Transition的状态置为STATE_STARTED,之前的状态为STATE_COLLECTING。

2)、调用Transition.applyReady。

继续调用了BLASTSyncEngine.setReady方法:

最终调用的是SyncGroup.setReady方法:

重要的其实就那一句,即将SyncGroup的成员变量mReady设置为true,顺便可以调用WindowSurfacePlacer.requestTraversal,看看SyncGroup是否完成,即它里面的所有参与者是否已经准备就绪(绘制完成)。

mReady这个成员变量还是很重要的,只有被设置为true,系统才会去检查这个SyncGroup是否完成。

4 小结

启动Transition这一节的内容还是挺多的,大概总结一下:

1)、在WMCore侧,调用ITransitionPlayer.requestStartTransition切换到WMShell。

2)、在WMShell侧,创建一个Transitions.ActiveTransition对象,为其找到一个后续执行动画的Transitions.TransitionHandler对象,接着将该Transitions.ActiveTransition对象添加到Transitions.mPendingTransitions中,后续动画就绪的时候,就是从Transitions.mPendingTransitions中拿需要play的Transitions.ActiveTransition对象。

3)、回到WMCore侧,得知在WMShell侧创建了和当前Transition一一对应的ActiveTransition后,这个Transition的状态就从STATE_COLLECTING切换为STATE_STARTED,并且相应的SyncGroup的成员变量mReady也被设置为true。

启动Transition的流程再总结的简单一点,就是之前在WMCore这一侧不是创建了一个Transition对象嘛,现在需要在WMShell这一侧创建一个相应的ActiveTransition。一旦这个ActiveTransition创建完成了,WMShell再告知WMCore,WMCore就知道WMShell那边的前序工作已经完成了,那就把Transition标记为STATE_STARTED,SyncGroup标记为ready。后续Transition满足进入下一个阶段的条件了,再看SyncGroup是不是ready了,如果ready了,就说明WMShell那也有一个ActiveTransition了,Transition就可以进入下一个阶段了。

5 从WMShell发起Transition

之前分析的流程是WMCore先创建一个Transition,然后WMShell再创建一个ActiveTransition。

还有一种方式是WMShell先创建ActiveTransition,然后WMCore再创建Transition,即Transitions.startTransition:

这个方法被很多地方调用,但都在WMShell:

可以认为这种方式是由WMShell发起了Transition,而我们之前分析的流程是WMCore发起了Transition。并且这里回调IWindowOrganizerController的是startNewTransition,而我们之前分析的流程回调的是startTransition,区别即在于传入的Transition的token是否为null:

而在WindowOrganizerController.startTransition中,判断传入的Transition为null,就说明流程是从WMShell发起的,才会去创建Transition对象:

看一下这里调用的TransitionController.startCollectOrQueue方法:

我们不讨论排队的情况,并且由于Transition流程是从WMShell发起,所以之前也没有创建过SyncGroup,所以这里是直接调用了TransitionController.moveToCollecting,这个我们之前已经分析过了,重点看下这里的OnStartCollect:

它接受一个boolean的参数,用来表示这个Transition的启动是否被推迟了,比较典型的场景是在某个TransitionA过程中灭屏,因为灭屏而新创建的这个TransitionB的启动会被推迟,需要等待TransitionA完成。TransitionA完成的时候TransitionB会被启动,即OnStartCollect的onCollectStarted方法在TransitionA结束的时候才被调用,而一般来说Transition在创建后很快就会被启动,因此这种场景下我们就可以认为TransitionB的启动其实是被推迟了,传参deferred就给一个true值。这个时候会检查TransitionB是否还需要启动,或者是被中止(如果TransitionA完成的时候屏幕已经被唤醒了),具体在RootWindowContainer.applySleepTokens:

这里只是大概提一嘴,不过多分析。

另外在我们分析的流程中,OnStartCollect的onCollectStarted方法是被立即调用的,即以下内容会立即执行:

1)、调用Transition.start,之前分析过不用多说。

2)、调用WindowOrganizerController.applyTransaction,因为Transition是从WMShell发起的,那么WMShell也要引起一些变化才行,不然WMCore这边不知道哪个WindowContainer变化了,哪些WindowContainer要参与动画。要引起变化,靠的就是WindowOrganizerController.applyTransaction,变化的信息则是保存在了WindowContainerTransaction,这方面也不细讲了,之前分析WindowContainerTransaction系列文章的时候有详细分析过。

相关推荐
阿甘知识库33 分钟前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道1 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
居居飒2 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He5 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗5 小时前
Android笔试面试题AI答之Android基础(1)
android
qq_397562316 小时前
android studio更改应用图片,和应用名字。
android·ide·android studio
峥嵘life7 小时前
Android Studio版本升级那些事
android·ide·android studio
新手上路狂踩坑7 小时前
Android Studio的笔记--BusyBox相关
android·linux·笔记·android studio·busybox
TroubleMaker9 小时前
OkHttp源码学习之retryOnConnectionFailure属性
android·java·okhttp
叶羽西11 小时前
Android Studio IDE环境配置
android·ide·android studio