发布网友 发布时间:2023-07-10 12:15
共1个回答
热心网友 时间:2024-12-04 17:39
假设有这样一个场景,一个是应用主窗口,一个是带有内容的SurfaceControl(简称SC),根据输入事件改变View控件以及SC图层的形态。
这个问题很简单,先监听事件,在分别设置各自的Geometry:
以上逻辑可以做到每次事件到来时,View 控件与SC 图层能够根据输入坐标保持统一的形态变化,如果当这个View的作用是为了遮住SC图层,上面的逻辑能够做到吗?显然是不能的,因为View的绘制与SC图层属性的生效是不同步的。SC具有快速生效的能力,而View控件的绘制需要时间,SF也需要等待GPU绘制完成才会做进一步的合成,这样就会导致快速滑动时View控件遮不住SC的情况。
之前的版本确实是无法达到这样的效果,但是随着BBQ机制的的介入,这样的需求就可以实现:
通过 applyTransactionOnDraw 方法就能够实现这样的需求:
frameworks/base/java/android/view/ViewRootImpl.java
实现原理就是监听下一帧绘制时机,将参数传递的事务所包含的修改合入到BBQ事务中,保证在同一帧生效:
以应用进入分屏模式为例,用户长按最近任务卡片,点击分屏按钮触发进入分屏的操作。看下系统是如何将SystemUI-系统服务-应用之间的事务操作进行同步。
启动分屏后,SystemUI进程通过 WindowOrganizer#applySyncTransaction() 方法发起原子请求,
WindowContainerTransaction 就是集合所有窗口配置更新的原子事务:
WindowContainerTransaction
收集一组要以原子方式应用的配置更改,可与 WindowContainerTransactionCallback 一起使用,以接收包含同步操作结果的事务。
frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
创建 SyncGroup 添加到 mActiveSyncs 待同步集合:
frameworks/base/services/core/java/com/android/server/wm/BLASTSyncEngine.java
frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
重点看下addToSync方法,首先将要同步的 WindowContainer 添加到指定 SyncId 的 SyncGroup 中,同时Task 容器的 mSyncState 修改为 SYNC_STATE_READY状态,WindowState 子窗口的 mSyncState 修改为 SYNC_STATE_WAITING_FOR_DRAW状态。
frameworks/base/services/core/java/com/android/server/wm/BLASTSyncEngine.java
将Task容器 mSyncState 状态修改为 SYNC_STATE_READY:
frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
将对应子窗口 mSyncState 状态修改为 SYNC_STATE_WAITING_FOR_DRAW:
frameworks/base/services/core/java/com/android/server/wm/WindowState.java
mSyncState 状态针对父容器与子窗口的状态作用是不同的,因为容器不参与绘制,能做的就是让自己处于随时等待同步的状态,因为校验时会从父容器到子窗口逐个校验,是否同步完成还是取决于子窗口的状态。而子窗口同步状态设置成 SYNC_STATE_WAITING_FOR_DRAW ,表示需要先等待窗口内容绘制完成。当窗口绘制完成会将应用的绘制同步到自己的 mSyncTransaction,然后再将窗口的同步状态改为SYNC_STATE_READY。这样才会正在触发* BLASTSyncEngine*的同步操作。
在 prepareSync 方法的最后给自己加了强制重绘的标记,在下次窗口布局刷新时,加窗口加入到resize的流程中,触发应用的UI绘制。
更新同步状态,同时申请WMS刷新布局进行更新。
应用端收到窗口尺寸变化的回调提醒,申请布局遍历,向WMS服务申请窗口布局。WMS服务处理完布局更新后,会去收集对应窗口配置更改的消费者,同时将 RELAYOUT_RES_BLAST_SYNC添加到返回应用端的结果标识中:
如果因其他功能业务针对应用窗口的 Geometry 修改操作需要与应用端 Buffer 的绘制在同一帧生效。可以通过WindowState#applyWithNextDraw() 方法将消费者添加到下一帧的同步中。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
应用进程完成布局更新的请求,之后的流程来到了应用端,在绘制周期内主要做了以下几件事:
frameworks/base/core/java/android/view/ViewRootImpl.java
触发时机是RT线程完成绘制提交GPU后,swap 缓冲区前。在这期间BBQ还未收到缓冲区可消费的提醒,此时 ViewRootImpl 收到回调通知让mRtBLASTSyncTransaction准备接管BBQ下一帧提交,同时暂停UI线程下一帧的操作以及接管后阻塞BBQ内部线程。
触发时机是 swap 缓冲区后,此时 mRtBLASTSyncTransaction 已包含了下一帧应用的所有数据更新。 ViewRootImpl 收到回调通知将 mRtBLASTSyncTransaction 事务所包含的所有操作合并到 mSurfaceChangedTransaction。
当所有元素执行完绘制流程,通知 WMS 服务应用下一帧内容绘制完成,同时传递等待提交的事务 mSurfaceChangedTransaction :
WMS服务收到应用绘制完成的提醒,重点工作就是将系统服务中其他消费者的相关修改与应用的下一帧内容同步到 mSyncTransaction:
postDrawTransaction包含应用已绘制的下一帧内容、系统服务中申请同步的消费者变更;
mSyncTransaction是窗口容器基类 WindowContainer 中的一个变量,主要用来处理各类型窗口的属性变更。
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
frameworks/base/services/core/java/com/android/server/wm/WindowState.java
每次窗口遍历都会对所有有改动的Surface的进行设置事务操作,如果容器正在状态不是
SYNC_STATE_NONE,就会将 SystemUI 提交的原子操作 mMainWindowSizeChangeTransaction 合入到每个容器的 mSyncTransaction 事务中,紧接着检查 BLASTSyncEngine 是否存在待同步事务:
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
这里的 mActiveSyncs 保存SystemUI 请求的待同步操作 SyncGroup,等待系统触发下一帧的同步校验,从而进一步收集各个窗口的同步数据。
frameworks/base/services/core/java/com/android/server/wm/BLASTSyncEngine.java
SyncGroup 如果已准备就绪,会遍历原子提交中所有存在配置更改的容器,检查其是否已准备好同步数据,或者是否是不可见的,达到其中一个要求则通过校验。
frameworks/base/services/core/java/com/android/server/wm/BLASTSyncEngine.java
SystemUI 此次原子操作所包含的对所有窗口配置更改的同步操作都已全部合入到 merged 事务,并通过远程回调将事务回传给SystemUI。
流程总结: