[076]SHELL TRANSITIONS

2022-12-09 14:12:30 浏览数 (1)

背景

最近在看一些问题的时候,尤其是一些事务切换闪屏的问题时候,发现对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,在调用SyncGrouponSurfacePlacement,遍历检查需要切换的窗口是否绘制完成。
代码语言: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系统了,希望未来能继续给大家带来我的原创文章。

0 人点赞