【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系列文章的时候有详细分析过。

相关推荐
selt7917 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
Yao_YongChao8 小时前
Android MVI处理副作用(Side Effect)
android·mvi·mvi副作用
非凡ghost8 小时前
JRiver Media Center(媒体管理软件)
android·学习·智能手机·媒体·软件需求
席卷全城8 小时前
Android 推箱子实现(引流文章)
android
齊家治國平天下9 小时前
Android 14 系统中 Tombstone 深度分析与解决指南
android·crash·系统服务·tombstone·android 14
maycho12311 小时前
MATLAB环境下基于双向长短时记忆网络的时间序列预测探索
android
思成不止于此11 小时前
【MySQL 零基础入门】MySQL 函数精讲(二):日期函数与流程控制函数篇
android·数据库·笔记·sql·学习·mysql
brave_zhao11 小时前
达梦数据库(DM8)支持全文索引功能,但并不直接兼容 MySQL 的 FULLTEXT 索引语法
android·adb
sheji341612 小时前
【开题答辩全过程】以 基于Android的网上订餐系统为例,包含答辩的问题和答案
android
easyboot12 小时前
C#使用SqlSugar操作mysql数据库
android·sqlsugar