前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[076]SHELL TRANSITIONS

[076]SHELL TRANSITIONS

作者头像
王小二
发布2022-12-09 14:12:30
1.4K0
发布2022-12-09 14:12:30
举报

背景

最近在看一些问题的时候,尤其是一些事务切换闪屏的问题时候,发现对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系统了,希望未来能继续给大家带来我的原创文章。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-12-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 一、学习资料
  • 二、BlastBufferQueue的优势
  • 三、SHELL TRANSITIONS
  • 四、SHELL TRANSITIONS的核心BLASTSyncEngine
    • 4.1 开启debug日志
      • 4.2 Pixel手机上抓取一次分屏后调整大小的debug日志
        • 4.3 BLASTSyncEngine的工作大体流程
        • 五、SHELL TRANSITIONS的未来
        • 尾巴-我的未来
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档