背景
最近在看一些问题的时候,尤其是一些事务切换闪屏的问题时候,发现对BlastBufferQueue了解的不够深入,就仔细研究了一下。 发现BlastBufferQueue和SHELL TRANSITIONS有很重要的关系,所以借此机会,来初步了解一下SHELL TRANSITIONS。
一、学习资料
做好先看懂这些资料。 BBQ 机制介绍:https://www.jianshu.com/p/50a30fa6952e BBQ 原理解读:https://www.jianshu.com/p/cdc60627df90 BBQ 运用场景:https://www.jianshu.com/p/384a5cd2e304 BLASTBufferQueue 详解 :https://blog.simowce.com/all-about-blastbbq
二、BlastBufferQueue的优势
1.Buffer申请在APP侧,减少SurfaceFlinger的压力 2.界面不显示,就会释放BlastBufferQueue对象,减少内存 3.将buffer和窗口信息更新可以同步,一次事务中可以传递buffer给sf,同时可以传递buffer对应的layer的窗口大小,crop等信息。
三、SHELL TRANSITIONS
SHELL TRANSITIONS是目前Android T上还没有完全开启的功能,部分窗口事务已经用上了SHELL TRANSITIONS(已知分屏的时候会用到)
如果你在T上遇到一些界面切换闪的问题,可以尝试开启一下这个功能,就会发现很多问题都解决了,当然目前google默认是关闭的,项目上直接开启,还是有很多风险的。
代码语言:javascript复制public static final String ENABLE_SHELL_TRANSITIONS = "persist.wm.debug.shell_transit";
四、SHELL TRANSITIONS的核心BLASTSyncEngine
BLASTSyncEngine是windowmanagerservice的一个成员对象,后续的每一次窗口事务切换都会围绕这个对象,也是SHELL TRANSITIONS的核心
代码语言:javascript复制private WindowManagerService(Context context, InputManagerService inputManager,
···
mSyncEngine = new BLASTSyncEngine(this);
···
}
通过一个实例来大概了解一下BLASTSyncEngine的工作流程
4.1 开启debug日志
代码语言:javascript复制adb shell wm logging enable-text WM_DEBUG_SYNC_ENGINE
4.2 Pixel手机上抓取一次分屏后调整大小的debug日志
Screenshot_20220921-113704.png
代码语言:javascript复制//开始一次窗口同步事务
09-21 10:58:49.248 1850 2855 V WindowManager: SyncGroup 24: Started for listener: com.android.server.wm.WindowOrganizerController@fd1c5e6
//添加需要同步的窗口
09-21 10:58:49.249 1850 2855 V WindowManager: SyncGroup 24: Adding to group: Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.249 1850 2855 V WindowManager: setSyncGroup #24 on Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.249 1850 2855 V WindowManager: SyncGroup 24: Adding to group: Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.249 1850 2855 V WindowManager: setSyncGroup #24 on Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
//确认同步已经ready
09-21 10:58:49.253 1850 2855 V WindowManager: SyncGroup 24: Set ready
//检查需要同步的窗口是否绘制ready了
09-21 10:58:49.254 1850 1974 V WindowManager: SyncGroup 24: onSurfacePlacement checking {Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}, Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}}
09-21 10:58:49.254 1850 1974 V WindowManager: SyncGroup 24: Unfinished container: Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.262 1850 3614 V WindowManager: SyncGroup 24: onSurfacePlacement checking {Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}, Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}}
09-21 10:58:49.262 1850 3614 V WindowManager: SyncGroup 24: Unfinished container: Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.265 1850 3614 V WindowManager: SyncGroup 24: onSurfacePlacement checking {Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}, Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}}
09-21 10:58:49.265 1850 3614 V WindowManager: SyncGroup 24: Unfinished container: Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.280 1850 3614 V WindowManager: onSyncFinishedDrawing Window{b324dee u0 com.android.settings/com.android.settings.Settings}
09-21 10:58:49.281 1850 1974 V WindowManager: SyncGroup 24: onSurfacePlacement checking {Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}, Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}}
09-21 10:58:49.281 1850 1974 V WindowManager: SyncGroup 24: Unfinished container: Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.281 1850 1974 V WindowManager: SyncGroup 24: onSurfacePlacement checking {Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}, Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}}
09-21 10:58:49.282 1850 1974 V WindowManager: SyncGroup 24: Unfinished container: Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.282 1850 3614 V WindowManager: SyncGroup 24: onSurfacePlacement checking {Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}, Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}}
09-21 10:58:49.282 1850 3614 V WindowManager: SyncGroup 24: Unfinished container: Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.289 1850 1974 V WindowManager: SyncGroup 24: onSurfacePlacement checking {Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}, Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}}
09-21 10:58:49.289 1850 1974 V WindowManager: SyncGroup 24: Unfinished container: Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.297 1850 3614 V WindowManager: onSyncFinishedDrawing Window{b7b1ef9 u0 com.android.chrome/com.google.android.apps.chrome.Main}
09-21 10:58:49.297 1850 2855 V WindowManager: onSyncFinishedDrawing Window{b7b1ef9 u0 com.android.chrome/com.google.android.apps.chrome.Main}
09-21 10:58:49.302 1850 1974 V WindowManager: SyncGroup 24: onSurfacePlacement checking {Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}, Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}}
//所有窗口已经ready,结束这次同步任务,
09-21 10:58:49.302 1850 1974 V WindowManager: SyncGroup 24: Finished!
//结束同步提交事务
09-21 10:58:49.302 1850 1974 V WindowManager: finishSync cancel=false for Task{4b04a1d #4 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.302 1850 1974 V WindowManager: finishSync cancel=false for Task{a07b58c #337 type=standard A=10160:com.android.chrome U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.302 1850 1974 V WindowManager: finishSync cancel=false for ActivityRecord{17979c4 u0 com.android.chrome/com.google.android.apps.chrome.Main} t337}
09-21 10:58:49.302 1850 1974 V WindowManager: finishSync cancel=false for Window{b7b1ef9 u0 com.android.chrome/com.google.android.apps.chrome.Main}
09-21 10:58:49.302 1850 1974 V WindowManager: finishSync cancel=false for Task{d19ee63 #5 type=standard ?? U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.302 1850 1974 V WindowManager: finishSync cancel=false for Task{775a61d #336 type=standard A=1000:com.android.settings.root U=0 rootTaskId=3 visible=true visibleRequested=true mode=multi-window translucent=false sz=1}
09-21 10:58:49.302 1850 1974 V WindowManager: finishSync cancel=false for ActivityRecord{809dae2 u0 com.android.settings/.Settings} t336}
09-21 10:58:49.303 1850 1974 V WindowManager: finishSync cancel=false for Window{b324dee u0 com.android.settings/com.android.settings.Settings}
4.3 BLASTSyncEngine的工作大体流程
4.3.1.调用startSyncSet
发起一次窗口事务同步任务,创建一个SyncGroup
,返回一个sync id
代码语言:javascript复制int startSyncSet(TransactionReadyListener listener, long timeoutMs, String name) {
final SyncGroup s = prepareSyncSet(listener, name);
startSyncSet(s, timeoutMs);
return s.mSyncId;
}
SyncGroup prepareSyncSet(TransactionReadyListener listener, String name) {
return new SyncGroup(listener, mNextSyncId , name);
}
void startSyncSet(SyncGroup s) {
startSyncSet(s, BLAST_TIMEOUT_DURATION);
}
void startSyncSet(SyncGroup s, long timeoutMs) {
mActiveSyncs.put(s.mSyncId, s);
}
4.3.2.收集下一次事务结束之后需要展示的窗口,调用addToSyncSet
,根据sync id将窗口添加到本次SyncGroup
中
代码语言:javascript复制void addToSyncSet(int id, WindowContainer wc) {
getSyncGroup(id).addToSync(wc);
}
private void addToSync(WindowContainer wc) {
if (!mRootMembers.add(wc)) {
return;
}
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Adding to group: %s", mSyncId, wc);
wc.setSyncGroup(this);
wc.prepareSync();
mWm.mWindowPlacerLocked.requestTraversal();
}
4.3.3.在逻辑事务结束之后调用setReady
,设置本次SyncGroup
已经ready,触发onSurfacePlacement
代码语言:javascript复制void setReady(int id) {
setReady(id, true);
}
void setReady(int id, boolean ready) {
getSyncGroup(id).setReady(ready);
}
private void setReady(boolean ready) {
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready", mSyncId);
mReady = ready;
if (!ready) return;
mWm.mWindowPlacerLocked.requestTraversal();
}
4.3.4.WMS的大遍历onSurfacePlacement阶段就会调用BLASTSyncEngine的onSurfacePlacement
,在调用SyncGroup
的onSurfacePlacement
,遍历检查需要切换的窗口是否绘制完成。
代码语言:javascript复制BLASTSyncEngine.java
void onSurfacePlacement() {
// backwards since each state can remove itself if finished
for (int i = mActiveSyncs.size() - 1; i >= 0; --i) {
mActiveSyncs.valueAt(i).onSurfacePlacement();
}
}
private void onSurfacePlacement() {
if (!mReady) return;
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: onSurfacePlacement checking %s",
mSyncId, mRootMembers);
for (int i = mRootMembers.size() - 1; i >= 0; --i) {
final WindowContainer wc = mRootMembers.valueAt(i);
if (!wc.isSyncFinished()) {//检查是否绘制完成
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Unfinished container: %s",
mSyncId, wc);
return;
}
}
finishNow();//一旦完成将会调用finishNow
}
4.3.5.一旦所有窗口绘制完成,将会调用finishNow
,完成事务的提交给sf,这样子这些窗口可以同时生效显示,解决绘制性不加或者事务错乱带来的闪屏问题。
代码语言:javascript复制private void finishNow() {
if (mTraceName != null) {
Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, mTraceName, mSyncId);
}
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Finished!", mSyncId);
SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
if (mOrphanTransaction != null) {
merged.merge(mOrphanTransaction);
}
for (WindowContainer wc : mRootMembers) {
wc.finishSync(merged, false /* cancel */);
}
final ArraySet<WindowContainer> wcAwaitingCommit = new ArraySet<>();
for (WindowContainer wc : mRootMembers) {
wc.waitForSyncTransactionCommit(wcAwaitingCommit);
}
class CommitCallback implements Runnable {
// Can run a second time if the action completes after the timeout.
boolean ran = false;
public void onCommitted() {
synchronized (mWm.mGlobalLock) {
if (ran) {
return;
}
mWm.mH.removeCallbacks(this);
ran = true;
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (WindowContainer wc : wcAwaitingCommit) {
wc.onSyncTransactionCommitted(t);
}
t.apply();
wcAwaitingCommit.clear();
}
}
// Called in timeout
@Override
public void run() {
// Sometimes we get a trace, sometimes we get a bugreport without
// a trace. Since these kind of ANRs can trigger such an issue,
// try and ensure we will have some visibility in both cases.
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionCommitTimeout");
Slog.e(TAG, "WM sent Transaction to organized, but never received"
" commit callback. Application ANR likely to follow.");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
onCommitted();
}
};
CommitCallback callback = new CommitCallback();
merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::onCommitted);
mWm.mH.postDelayed(callback, BLAST_TIMEOUT_DURATION);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady");
mListener.onTransactionReady(mSyncId, merged);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
mActiveSyncs.remove(mSyncId);
mWm.mH.removeCallbacks(mOnTimeout);
// Immediately start the next pending sync-transaction if there is one.
if (mActiveSyncs.size() == 0 && !mPendingSyncSets.isEmpty()) {
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "PendingStartTransaction found");
final PendingSyncSet pt = mPendingSyncSets.remove(0);
pt.mStartSync.run();
if (mActiveSyncs.size() == 0) {
throw new IllegalStateException("Pending Sync Set didn't start a sync.");
}
// Post this so that the now-playing transition setup isn't interrupted.
mWm.mH.post(() -> {
synchronized (mWm.mGlobalLock) {
pt.mApplySync.run();
}
});
}
}
五、SHELL TRANSITIONS的未来
BlastBufferQueue将单个窗口的buffer和窗口属性修改同步 BLASTSyncEngine将多个窗口的修改提交同步 SHELL TRANSITIONS将系统中所有窗口事务切换统一(启动,转屏,分屏,画中画等) 可以预料的是SHELL TRANSITIONS将会是Android T之后未来一步大棋,单独用一个库来管理这部分的逻辑。
代码语言:javascript复制frameworks/base/libs/WindowManager/Shell/
尾巴-我的未来
细心的读者已经发现王小二的Android站
已经改成了王小二的技术栈
,因为未来的我将不仅仅局限于Android系统了,希望未来能继续给大家带来我的原创文章。