网站建立好如何做seo,网站接入服务商查询,慈溪企业网站建设公司,对于ICP而言 主要承担网站信息这一节的内容在WMCore中#xff0c;回想我们的场景#xff0c;是在Launcher启动某一个App#xff0c;那么参与动画的就是该App对应Task#xff08;OPEN#xff09;#xff0c;以及Launcher App对应的Task#xff08;TO_BACK#xff09;。在确定了动画的参与者后#x…
这一节的内容在WMCore中回想我们的场景是在Launcher启动某一个App那么参与动画的就是该App对应TaskOPEN以及Launcher App对应的TaskTO_BACK。在确定了动画的参与者后接下来我们就需要等待动画参与者绘制完成。一个基本的逻辑是参与动画的主体要绘制出来了才能开始动画不然动画都开始执行了窗口还没有绘制出来那对于用户来说屏幕上是没有任何变化的这不就有点尴尬了。
ShellTransitions之前检查动画是否可以开始的逻辑是在AppTransitionController.handleAppTransitionReady中通过调用transitionGoodToGo来检查窗口是否绘制完成的现在则是在BLASTSyncEngine.onSurfacePlacement中通过调用BLASTSyncEngine.SyncGroup.tryFinish不断检查所有动画参与者是否已经全部同步完毕。一旦所有的动画参与者完成同步则视为SyncGroup完成或者说Transition就绪就会调用BLASTSyncEngine.SyncGroup.finishNow最终走到Transition.onTransactionReady具体的调用堆栈为
RootWindowContainer.performSurfacePlacementNoTrace
- BLASTSyncEngine.onSurfacePlacement
- BLASTSyncEngine.SyncGroup.tryFinish
- BLASTSyncEngine.SyncGroup.finishNow
- Transition.onTransactionReady
我们这一节先分析检查SyncGroup是否完成BLASTSyncEngine.SyncGroup.tryFinish以及SyncGroup完成BLASTSyncEngine.SyncGroup.finishNow的内容Transition就绪的内容即Transition.onTransactionReady放到单独一章分析。
1 BLASTSyncEngine.onSurfacePlacement
BLASTSyncEngine.onSurfacePlacement由RootWindowContainer.performSurfacePlacementNoTrace调用代码为 BLASTSyncEngine的成员变量mActiveSyncs之前已经介绍过了当我们创建Transition的时候也会创建一个SyncGroup来收集参与动画的WindowContainer创建的SyncGroup则保存在了BLASTSyncEngine.mActiveSyncs。
这里则是从BLASTSyncEngine.mActiveSyncs中拿出SyncGroup调用SyncGroup.tryFinish来检查SyncGroup是否完成。
2 SyncGroup.tryFinish 1、如果SyncGroup.mReady为false则直接返回。之前我们分析Transition的启动流程时知道了只有当WMShell侧创建了一个ActiveTransition后切换回WMCore并且调用Transition.startTransition才算正式启动正是在Transition.start中调用了SyncGroup.setReady方法将SyncGroup的mReady设置为了true。从这里我们就看到了SyncGroup.mReady的作用如果Transition没有启动那么这里是不会去检查其对应的SyncGroup是否完成了的而是直接返回false也就是说如果SyncGroup没有ready那么Transition将无法走到下一个阶段。
2、SyncGroup.mRootMembers则保存了参与动画的WindowContainer我们这里则是为每一个WindowContainer调用WindowContainer.isSyncFinished来检查这个WindowContainer是否完成同步/绘制只要有一个没有完成同步那么就直接返回false不需要往下走了等待下一次RootWindowContainer.performSurfacePlacementNoTrace到来的时候再检查看看有没有完成同步。
3、如果所有参与动画的WindowContainer都已经完成同步了那么就继续调用SyncGroup.finishNow来将当前SyncGroup结束掉。
我们先分析WindowContainer.isSyncFinished再去分析SyncGroup.finishNow。
2.1 WindowContainer.isSyncFinished
首先再回顾一下当把WindowContainer添加SyncGroup的时候会为每一个WindowContainer调用prepareSync方法结果是
1、WindowState类型的WindowContainer其mSyncState被置为SYNC_STATE_WAITING_FOR_DRAW。
2、非WindowState类型的WindowContainer其mSyncState被置SYNC_STATE_READY。
再看WindowContainer.isSyncFinished的内容 1、首先一个基本的规则是如果一个WindowContainer请求的是不可见的那么将其视为同步完成。这个其实也很好理解如果这个WindowContainer在完成动画的时候是不希望被显示的那么就不需要等待它绘制完成了。
2、对于非WindowState类型的WindowContainer比如Task或者ActivityRecord由于它们的mSyncState一开始就被设置为了SYNC_STATE_READY因此它们主要是检查它们的mChildren是否同步完成最终检查的就是其中的WindowState是否完成同步/绘制。一旦有一个WindowState
同步完成。请求可见。和父容器大小相等。
只要找到一个符合以上条件的WindowState那么就可以认为这个WindowContainer已经完成了同步。
3、对于WindowState则只有一个标准即其mSyncState是否是SYNC_STATE_READY暂不考虑子窗口的情况。
2.2 WindowContainer.onSyncFinishedDrawing
再看下什么时候WindowState的mSyncState什么被设置为SYNC_STATE_READY。
WindowState的mSyncState被设置为SYNC_STATE_READY的地方只有一处在WindowContainer.onSyncFinishedDrawing 从注释也能看出当WindowContainer完成绘制其内容的时候这个方法会被调用。
具体调用的地方则是 很明显当窗口完成绘制时会调用WindowState.finishDrawing进而将WindowState的mSyncState设置为SYNC_STATE_READY。
而一旦Task/ActivityRecord中的WindowState绘制完成那么该Task/ActivityRecord就会被视为同步完成。
这部分的内容之前分析WindowContainerTransaction的文章有过更加详细的介绍
4【Android 12】【WCT的同步】BLASTSyncEngine - 掘金 (juejin.cn)
更多的详细内容可以看下当时的分析。
3 SyncGroup.finishNow
一旦SyncGroup中所有的动画参与者都同步完成那么就调用SyncGroup.finishNow来结束掉这个SyncGroup。
这个方法是我们本篇文章的重点且内容较多我们分段去看。
3.1 合并Transaction 这里从对象工厂中拿到一个Transaction对象局部变量merged对于所有参与动画的WindowContainer将它们在动画期间发生的同步操作都合并到这个局部变量merged中。
这一点主要是通过对所有参与动画的WindowContainer调用WindowContainer.finishSync方法来完成的indowContainer.finishSync方法内容为 1、将WindowContainer.mSyncTransaction中收集到的对当前WindowContainer对应的SurfaceControl的修改同步操作合并到传参outMergedTransaction中即我们上面提到的SyncGroup.finishNow中的局部变量merged。
2、递归调用所有子WindowContainer的finishSync方法最终的结果是将这个WindowContainer以及所有子WindowContainer的同步操作都收集到传参outMergedTransaction中。
3、最后由于同步工作已经结束那么将这个WindowContainer的mSyncState以及mSyncGroup之类的成员变量进行重置。
这里所说的同步操作主要是针对WindowContainer.mSyncTransaction来说的其实之前也看到过“sync”这个字眼了我们这里来大致说明一下“同步”这个概念。
3.1.1 “同步”的概念
其实最早的时候BLASTSyncEngine以及SyncGroup并不是用于动画而是和WindowContainerTransaction一起结合使用主要是用于分屏。
分屏由于将屏幕一分为二以供两个App同时显示那么一旦分屏发生变化进退分屏调整分割线位置等那么最起码就会有两个可见的SurfaceControl参与了变化即参与分屏的这两个App下的SurfaceControl。
那么一个很明显能够想到的问题是如何做到这两个分屏的App界面能够一起改变呢比如我调整分屏分割线的位置我肯定不希望看到上分屏的App界面改变之后下分屏的App界面没有跟着一起改变而是又过了几百毫秒才开始变化。这是一个肯定会遇到的问题App界面改变是在窗口绘制完成之后而WMS无法控制窗口绘制的时间因此如果WMS不加以控制那么就会出现由于两个窗口的绘制时间不同导致用户看到的两个界面先后进行了改变异步而非同一时间进行了改变同步。
因此当时引出了BLASTSyncEngine以及SyncGroup的概念这套机制最开始就是用来保证使用WindowContainerTransaction的模块比如分屏可以做到SurfaceControl的同步。
同步的大致做法则是创建一个统一的Transaction对象即SyncGroup.finishNow中创建的那个Transaction类型的局部变量merged来收集所有参与到分屏的SurfaceControl的变化并且只有等到所有参与分屏的窗口都绘制完成后才对这个Transaction对象调用apply方法这样就保证了所有的SurfaceControl变化在一次Transaction.apply中进行了提交。
从以上介绍可以看到要实现同步有两个比较重要点一是有一个统一的Transaction来收集所有SurfaceControl的变化二是当所有参与同步的窗口绘制完成后再调用Transaction.apply。
更多的内容可以去看下之前分析WindowContainerTransaction的系列文章。
3.1.2 WindowContainer.mSyncTransaction
回到现在Android14的ShellTransitions中来现在是动画也开始用BLASTSyncEngine这一套逻辑了接下来看下现在动画是如何实现同步的。
从上面的介绍中我们知道同步的两个重要的点是
1、有一个统一的Transaction来收集所有SurfaceControl的变化。
2、当所有参与同步的窗口绘制完成后再调用这个统一的Transaction对象的Transaction.apply方法。
可知和这个Transaction是有很大的关系。
第二点放到后面再说这一节我们分析一下第一点即有一个统一的Transaction来收集所有SurfaceControl的变化。
之前看SyncGroup.finishNow我们知道了这个统一的Transaction就是这里创建的Transaction类型的局部变量merged它合并了所有参与动画的WindowContainer的mSyncTransaction中收集的内容那么WindowContainer.mSyncTransaction又是什么
再举一个例子来看一个经典的对SurfaceControl进行显示的操作为WindowSurfaceController.showRobustly 根据以往的系列文章知WindowSurfaceController.mSurfaceControl对应的是SurfaceFlinger侧的BufferStateLayer。
调用Transaction.show的时候只是将对SurfaceControl的操作暂存在了Transaction中更准确的说是native层的layer_state_t结构体中只有当调用Transaction.apply的时候这个对SurfaceControl的操作才算真正提交到了SurfaceFlinger端进而作用到了Layer上。
那么我们再看下这个传参Transaction对象是从哪里拿到的一般来说调用堆栈为
WindowState.prepareSurfaces
- WindowStateAnimator.prepareSurfaces
- WindowSurfaceController.showRebustly
看到是在WindowState.prepareSurfaces中通过WindowContainer.getSyncTransaction拿到的 WindowContainer.getSyncTransaction为 如果WindowContainer.mSyncTransactionCommitCallbackDepth大于0或者WindowContainer.mSyncTransaction不为SYNC_STATE_NONE说明此时WindowContainer仍然处于需要同步的场景中因此返回WindowContainer.mSyncTransaction否则返回WindowContainer.getPendingTransaction。
WindowContainer.getPendingTransaction为 一般返回的是DisplayContent的mPendingTransaction。
再看下mSyncTransaction和mPendingTransaction的定义以及初始化 看到mSyncTransaction和mPendingTransaction其实都是一个普通的Transaction对象本质上没有区别区别在于它们的使用方式
1、pendingTransaction基本上每次RootWindowContainer.performSurfacePlacementNoTrace就apply一次 可以认为是使用pendingTransaction对SurfaceControl操作后很快就会调用Transaction.apply也就是说使用pendingTransaction对SurfaceControl进行的操作很快就能见到效果。
2、syncTransaction的apply方法的调用时机则是和Transition的流程密切相关只有走到特定的阶段才会调用Transaction.apply方法以后的分析中我们会看到。
最后一句话总结一下pendingTransaction和syncTransaction的区别就是需要WindowContaienr同步的场景使用syncTransaction不需要WindowContainer同步的场景则使用pendingTransaction…怎么有点像废话呢
3.2 注册TransactionCommittedListener回调以及超时处理 主要内容是
1、为所有参与到动画的WindowContainer调用waitForSyncTransactionCommit方法.
2、定义一个CommitCallback的类这个类有一个自定义的onCommitted方法以及复写Runnable的run方法。
3、创建一个CommitCallback类的对象callback。
4、调用Transaction.addTransactionCommittedListener方法注册TransactionCommittedListener回调回调触发的时候执行这个callback的onCommitted方法。
5、Handler.postDelayed将这个callback添加到了MessageQueue中5000ms超时之后执行这个callback的run方法。
接下来分别介绍并不一定按照顺序。
3.2.1 注册TransactionCommittedListener回调 CommitCallback callback new CommitCallback();merged.addTransactionCommittedListener(Runnable::run,() - callback.onCommitted(new SurfaceControl.Transaction()));看到这里为merged调用了Transaction.addTransactionCommittedListener方法 从注释来看TransactionCommittedListener的onTransactionCommitted回调方法会在Transaction被apply的时候调用另外这个回调被执行的时候也说明当前Transaction将不会被一个新的Transaction对象复写。
那么再结合SyncGroup.finishNow的代码也就是说当merged这个Transaction对象被apply后Transaction.addTransactionCommittedListener这段代码将被执行
executor.execute(listener::onTransactionCommitted)也就是异步执行传参listener的onTransactionCommitted方法即SyncGroup.finishNow中的这段代码
callback.onCommitted(new SurfaceControl.Transaction())即当merged这个Transaction对象被apply后这里定义的CommitCallback类的onCommitted方法将会被执行。
分析了注册TransactionCommittedListener回调后我们可以再回过头来看CommitCallback类的定义即它的onCommitted方法和run方法。
3.2.2 CommitCallback.onCommitted
先看onCommitted方法从上面的分析我们知道这个方法将会在merged被apply的时候调用作用为
1、将CommitCallback从MessageQueue中移除即merged在规定的5000ms内得到apply了那么就不需要触发超时了。
2、将ran这个变量置为true因为Transaction有可能在5000ms超时后才apply那么onCommitted方法就有可能走两次。
3、调用WindowContainer的onSyncTransactionCommitted方法onSyncTransactionCommitted方法要和waitForSyncTransactionCommit结合着来看 正好WindowContainer.waitForSyncTransactionCommit方法也是在上面被调用了感觉这两个方法主要是对mSyncTransactionCommitCallbackDepth这个成员变量进行操作而mSyncTransactionCommitCallbackDepth作用的地方也在我们之前看过的WindowContainer.getSyncTransaction中 我自己的看法是这个变量用来继续延长WindowContainer的同步状态。
如我们之前第一节合并Transaction中提到的这里会为所有参与同步的WindowContainer调用WindowContainer.finishSync方法这将会使得WindowContainer的mSyncState重置为SYNC_STATE_NONE那么假如没有mSyncTransactionCommitCallbackDepth此时调用WindowContainer.getSyncTransaction将会返回pendingTransaction而非syncTransaction也就是说WindowContainer的同步状态在走到SyncGroup.finishNow的时候就结束了。
而加入了mSyncTransactionCommitCallbackDepth之后WindowContainer的同步状态的结束将会被延迟到merged被apply的时候。
为什么要这么做呢因为此时SyncGroup.finishNow距离merged被apply还有一段时间而且这个时间其实可能会超过5000ms即上面规定的超时时限。假如在merged被apply之前WindowContainer又发生了变化那么如果没有mSyncTransactionCommitCallbackDepth的存在此时WindowContainer将使用pendingTransaction并且pendingTransaction如果再在merged被apply之前就apply就会出现新的TransactionpendingTransaction的内容被旧的TransactionsyncTransaction内容覆盖的情况。
4、调用WindowContainer.onSyncTransactionCommitted将所有参与动画的WindowContainer.mSyncTransaction收集到Transaction类型传参t中集中进行一次apply。
如之前所说此时距离merged被apply还有一段时间在这段时间内参与到动画的WindowContainer是有可能继续发生变化的而syncTransaction合并到merged的操作已经结束了为了让这个时间段的变化也能够被应用所以这里调用WindowContainer.mSyncTransaction将收集到变化的syncTransaction都合并到一个Transaction中然后调用apply。
但是这样不就是后发生变化的WindowContainer的Transaction先被apply了吗这样不是还会出现上面提到的Transaction被覆盖的情况目前我暂时没有碰到过这种情况但是这个逻辑我感觉是有问题的。
3.2.3 CommitCallback.run
根据我们的分析我们知道了这个方法将会在5000ms超时后调用主要的内容是
1、调用TransactionReadyListener.onTransactionCommitTimeout通知关心方超时的情况。
2、调用CommitCallback.onCommitted方法应该是想让syncTransaction收集到的变化得到应用但是之前合并到merged那部分变化则是永久丢失掉了这部分应该才是最重要的。
3、这里打印了一条log Slog.e(TAG, WM sent Transaction to organized, but never received commit callback. Application ANR likely to follow.);打印了这个条log的时候我们已经知道是处于5000ms超时的情况了那么可能会出现本来应该显示的Layer在5000ms的时间内得不到显示那么屏幕上就可能会出现没有任何一个输入窗口可以作为焦点窗口的情况输入窗口能够接收焦点需要其Layer为可见如果此时再来一个KeyEvent事件那么就会发生无焦点窗口的ANR。
3.3 调用Transition.onTransactionReady
只剩下最后一点内容了一起来看下 1、调用TransactionReadyListener类型的mListener的onTransactionReady方法。
2、将当前SyncGroup从BLASTSyncEngine.mActiveSyncs中移除。
3、将其成员变量mOnTimeout从MessageQueue中移除。
有关其成员变量mListener以及mOnTimeout的部分在之前创建SyncGroup对象的时候漏说了现在大概过一遍。
首先看下SyncGroup的构造方法 1、int类型的mSyncId成员变量保存该SyncGroup的ID。
2、TransactionReadyListener类型的成员变量mListener保存与该SyncGroup一一对应的Transition对象TransactionReadyListener定义为 用来通知Transition同步完成以及Transaction提交超时。
所以这里调用的是Transition.onTransactionReadyTransition.onTransactionReady的内容比较多需要单独开一篇分析。
3、mOnTimeout则是一个Runnable用来在超时的时候触发BLASTSyncEngine.onTImeout方法 主要内容就是遍历一下参与同步的WindowContainer看下是哪个WindowContainer没有同步完成以及在方法的最终调用SyncGroup.finishNow这个一点也很好理解毕竟我们不能无限等待某一个WindowState绘制完成。
注意区分一下这里的超时和我们上面提到的超时。
这里的超时是在SyncGroup刚刚创建或者说Transition刚开始收集的时候开始计时的防止某一个窗口迟迟没有完成绘制从而无限等待这个窗口绘制完成的情况。
上面提到的超时是在SyncGroup.finishNow的时候开始计时的防止merged这个Transaction迟迟没有得到applyTransition没有走到下一个阶段从而syncTransaction的收集的变化内容无法被apply的情况。