这一节的内容涉及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一起使用的流程如下:
- WMCore启动一个Activity并调用requestStartTransition方法。
- 这个TransitionPlayer的实现执行相应操作,然后调用IWindowOrganizerController的startTransition方法,告诉WMCore正式开始(在此之前,WMCore将收集Transition中的所有更改,但不会认为Transition已准备好进行动画)。
- 一旦Transition中所有收集的更改都完成绘制,WMCore将调用onTransitionReady方法,在这里委托实际的动画。
- 一旦这个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系列文章的时候有详细分析过。