4【Android 12】【WCT的同步】BLASTSyncEngine

在这里插入图片描述

一、BLASTSyncEngine定义

/**
 * Utility class for collecting WindowContainers that will merge transactions.
 * For example to use to synchronously resize all the children of a window container
 *   1. Open a new sync set, and pass the listener that will be invoked
 *        int id startSyncSet(TransactionReadyListener)
 *      the returned ID will be eventually passed to the TransactionReadyListener in combination
 *      with a set of WindowContainers that are ready, meaning onTransactionReady was called for
 *      those WindowContainers. You also use it to refer to the operation in future steps.
 *   2. Ask each child to participate:
 *       addToSyncSet(int id, WindowContainer wc)
 *      if the child thinks it will be affected by a configuration change (a.k.a. has a visible
 *      window in its sub hierarchy, then we wi`ll increment a counter of expected callbacks
 *      At this point the containers hierarchy will redirect pendingTransaction and sub hierarchy
 *      updates in to the sync engine.
 *   3. Apply your configuration changes to the window containers.
 *   4. Tell the engine that the sync set is ready
 *       setReady(int id)
 *   5. If there were no sub windows anywhere in the hierarchy to wait on, then
 *      transactionReady is immediately invoked, otherwise all the windows are poked
 *      to redraw and to deliver a buffer to {@link WindowState#finishDrawing}.
 *      Once all this drawing is complete, all the transactions will be merged and delivered
 *      to TransactionReadyListener.
 *
 * This works primarily by setting-up state and then watching/waiting for the registered subtrees
 * to enter into a "finished" state (either by receiving drawn content or by disappearing). This
 * checks the subtrees during surface-placement.
 */
class BLASTSyncEngine

渣翻:

用于收集将merge transactions的WindowContainers 的工具类。

例如用于同步调整一个WindowContainer下的所有子WindowContainer的尺寸 :

1)、打开一个新的同步集,并传递将被调用的listener:

int id startSyncSet(TransactionReadyListener) 

返回的ID最终会与一组准备好的WindowContainers一起传递给TransactionReadyListener,这意味那些WindowContainers已经调用了onTransactionReady。您还可以使用它来指代以后步骤中的操作。

2)、 要求每个子容器参与:

 addToSyncSet(int id, WindowContainer wc)

如果子容器认为它会受到Configuration更改的影响(也就是在其子层次结构中有一个可见的窗口,那么我们将增加一个预期的计数器回调)。此时,容器层次结构会将 pendingTransaction 和子层次结构更新重定向到同步引擎。

3)、将configurationchanges应用到WindowContainers。

4)、告诉BLASTSyncEngine同步集已准备好:

setReady(int id)

5)、如果层次结构中的任何地方都没有子窗口等待,则立即调用transactionReady,否则所有窗口都将被推动去重绘和去给WindowState#finishDrawing传送缓冲区。一旦所有这些绘制完成,所有transaction将被合并并且交付给 TransactionReadyListener。

这主要通过设置状态然后观察/等待注册的子树进入“完成”状态(通过接收绘制的内容或消失)来工作。这会在surface-placement期间检查子树。

根据WindowOrganizerController的情况看下1-5条的执行情况。

二、同步流程分析

在分析WindowContainerTransaction的应用时,关于WindowContainerTransaction#applySyncTransaction方法,当时是跳过了很多内容直接去分析WindowContainerTransaction是如何应用到WindowContainer上了,那些跳过的内容也就是和同步机制相关的,这次就来跟踪下同步的流程。

    @Override
    public int applySyncTransaction(WindowContainerTransaction t,
            IWindowContainerTransactionCallback callback) {
        enforceTaskPermission("applySyncTransaction()");
        if (t == null) {
            throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
        }
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                /**
                 * If callback is non-null we are looking to synchronize this transaction by
                 * collecting all the results in to a SurfaceFlinger transaction and then delivering
                 * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the
                 * details of the operation. But at a high level we create a sync operation with a
                 * given ID and an associated callback. Then we notify each WindowContainer in this
                 * WindowContainer transaction that it is participating in a sync operation with
                 * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady"
                 * which means that we have added everything to the set. At any point after this,
                 * all the WindowContainers will eventually finish applying their changes and notify
                 * the BLASTSyncEngine which will deliver the Transaction to the callback.
                 */
                int syncId = -1;
                if (callback != null) {
                    syncId = startSyncWithOrganizer(callback);
                }
                applyTransaction(t, syncId, null /*transition*/);
                if (syncId >= 0) {
                    setSyncReady(syncId);
                }
                return syncId;
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

1 调用BLASTSyncEngine#startSyncSet创建SyncGroup

WindowOrganizerController#applyTransaction的根据之前的分析,已经知道了,就是为WindowContainer应用WindowContainerTransaction中的设置。

在applyTransaction之前,有这样一个操作:

    @Override
    public int applySyncTransaction(WindowContainerTransaction t,
            IWindowContainerTransactionCallback callback) {
	    ......
        try {
            synchronized (mGlobalLock) {
                /**
                 * If callback is non-null we are looking to synchronize this transaction by
                 * collecting all the results in to a SurfaceFlinger transaction and then delivering
                 * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the
                 * details of the operation. But at a high level we create a sync operation with a
                 * given ID and an associated callback. Then we notify each WindowContainer in this
                 * WindowContainer transaction that it is participating in a sync operation with
                 * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady"
                 * which means that we have added everything to the set. At any point after this,
                 * all the WindowContainers will eventually finish applying their changes and notify
                 * the BLASTSyncEngine which will deliver the Transaction to the callback.
                 */
                int syncId = -1;
                if (callback != null) {
                    syncId = startSyncWithOrganizer(callback);
                }
                applyTransaction(t, syncId, null /*transition*/);
			   ......
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

这里,如果是通过WindowOrganizer#applyTransaction,那么这里传入callback的就是null。

如果是WindowOrganizer#applySyncTransaction,就会传入一个callback,之前分析WCT的发送的时候,知道这里的callback是一个SyncCallback类型的回调。接下来看下callback不为空的情况。

    @VisibleForTesting
    int startSyncWithOrganizer(IWindowContainerTransactionCallback callback) {
        int id = mService.mWindowManager.mSyncEngine.startSyncSet(this);
        mTransactionCallbacksByPendingSyncId.put(id, callback);
        return id;
    }

这里调用了BLASTSyncEngine#startSyncSet,BLASTSyncEngine#startSyncSet返回了一个唯一的ID,接着把这个ID和callback存储到HashMp类型的mTransactionCallbacksByPendingSyncId中去,那么可以知道ID和callback应该是一一对应的关系。后续当WM端的transaction完成后,在WindowOrganizerController#onTransactionReady中会根据ID再取回callback。

接着看下BLASTSyncEngine#startSyncSet都干了什么。

    int startSyncSet(TransactionReadyListener listener) {
        final int id = mNextSyncId++;
        final SyncGroup s = new SyncGroup(listener, id);
        mActiveSyncs.put(id, s);
        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started for listener: %s", id, listener);
        return id;
    }

1)、将mNextSyncId赋值给id,然后自增1。

private int mNextSyncId = 0;

这里看到,mNextSyncId是BLASTSyncEngine的成员变量,由于BLASTSyncEngine只在WMS的构造器中创建了一次,唯一实例由WMS持有,那么可以知道mNextSyncId也是全局唯一一份,每次BLASTSyncEngine#startSyncSet都会自增1,保证了ID的唯一性。

2)、以这个唯一ID和传入的WindowOrganizerController对象为基础,创建了一个SyncGroup对象。

        private SyncGroup(TransactionReadyListener listener, int id) {
            mSyncId = id;
            mListener = listener;
        }

SyncGroup是BLASTSyncEngine的内部类,创建的唯一位置便是在BLASTSyncEngine#startSyncSet。

3)、将ID和创建的SyncGroup对象加入到mActiveSyncs队列中。

private final SparseArray<SyncGroup> mActiveSyncs = new SparseArray<>();

mActiveSyncs是BLASTSyncEngine的成员变量。

4)、最后,将这个具有唯一性的ID返回。

那么这个ID的值是唯一的,对应了一个SyncGroup对象,也对应了一个callback。

2 调用BLASTSyncEngine#addToSyncSet将所有参与同步的WindowContainer添加到SyncGroup中

在WindowOrganizerController#startSyncWithOrganizer之后,便是调用WindowOrganizerController#applyTransaction来为WindowContainer应用WindowContainerTransaction设置的修改(即Configuration更改,层级调整之类的):

    /**
     * @param syncId If non-null, this will be a sync-transaction.
     * @param transition A transition to collect changes into.
     */
    private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
            @Nullable Transition transition) {
		......
        try {
			......
            Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
                    t.getChanges().entrySet().iterator();
            while (entries.hasNext()) {
				......
                // Make sure we add to the syncSet before performing
                // operations so we don't end up splitting effects between the WM
                // pending transaction and the BLASTSync transaction.
                if (syncId >= 0) {
                    addToSyncSet(syncId, wc);
                }
                if (transition != null) transition.collect(wc);

                int containerEffect = applyWindowContainerChange(wc, entry.getValue());
			   ......
            }
			......
        } finally {
            mService.continueWindowLayout();
        }
    }

然而在调用WindowOrganizerController#applyWindowContainerChange方法应用之前修改之前,还有一个重要的步骤:

                // Make sure we add to the syncSet before performing
                // operations so we don't end up splitting effects between the WM
                // pending transaction and the BLASTSync transaction.
                if (syncId >= 0) {
                    addToSyncSet(syncId, wc);
                }

这个方法目前看到有三处需要执行,一是在WindowOrganizerController#applyWindowContainerChange之前,二是在WindowOrganizerController#reparentChildrenTasksHierarchyOp之前,三是在WindowOrganizerController#sanitizeAndApplyHierarchyOp之前。

接下来分析WindowOrganzierController#addToSyncSet的内容:

    @VisibleForTesting
    void addToSyncSet(int syncId, WindowContainer wc) {
        mService.mWindowManager.mSyncEngine.addToSyncSet(syncId, wc);
    }

WindowOrganizerController#addToSyncSet调用了BLASTSyncEngine#addToSyncSet。

    void addToSyncSet(int id, WindowContainer wc) {
        mActiveSyncs.get(id).addToSync(wc);
    }

在第一小节中我们创建了SyncGroup对象,并且和ID是一一对应,保存在了BLASTSyncEngine的成员变量mActiveSyncs中,所以这里就可以通过ID从mActiveSyncs中拿到对应的SyncGroup。

那么这里调用的就是SyncGroup#addToSyncSet:

        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();
        }

接下来的内容就是,之前BLASTSyncEngine注释中所提到的,将所有参与同步的WindowContainer都加入到同步集中,参与同步的WindowContainer,也就是WindowContainerTransaction中保存的那些客户端指定的WindowContainer。

逐步分析SyncGroup#addToSync的内容。

2.1 将传入的WindowContainer加入到SyncGroup的mRootMembers队列

            if (!mRootMembers.add(wc)) {
                return;
            }

mRootMembers是SyncGroup的成员变量:

final ArraySet<WindowContainer> mRootMembers = new ArraySet<>();

这里使用ArraySet保证WindowContainer只会被添加一次,毕竟我们上面看到,入口有三处。

后续SyncGroup通过遍历mRootMembers队列来判断所有参与同步的WindowContainer是否完成同步。

2.2 WindowContainer#setSyncGroup

    void setSyncGroup(@NonNull BLASTSyncEngine.SyncGroup group) {
        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "setSyncGroup #%d on %s", group.mSyncId, this);
        if (group != null) {
            if (mSyncGroup != null && mSyncGroup != group) {
                throw new IllegalStateException("Can't sync on 2 engines simultaneously");
            }
        }
        mSyncGroup = group;
    }

很简单,将WindowContainer.mSyncGroup指向当前SyncGroup。

WindowContainer的成员变量mSyncGroup的定义是:

    /**
     * If non-null, references the sync-group directly waiting on this container. Otherwise, this
     * container is only being waited-on by its parents (if in a sync-group). This has implications
     * on how this container is handled during parent changes.
     */
    BLASTSyncEngine.SyncGroup mSyncGroup = null;

如果非空,说明其SyncGroup正在等待此WindowContainer。否则,这个WindowCOntainer只会被它的父WindowContainer等待(如果在SyncGroup中)。这会影响在父WindowContainer更改期间这个WindowContainer要如何被处理。

2.3 WindowContainer#prepareSync

    /**
     * Prepares this container for participation in a sync-group. This includes preparing all its
     * children.
     *
     * @return {@code true} if something changed (eg. this wasn't already in the sync group).
     */
    boolean prepareSync() {
        if (mSyncState != SYNC_STATE_NONE) {
            // Already part of sync
            return false;
        }
        for (int i = getChildCount() - 1; i >= 0; --i) {
            final WindowContainer child = getChildAt(i);
            child.prepareSync();
        }
        mSyncState = SYNC_STATE_READY;
        return true;
    }

这里新增了一个WindowContainer.mSyncState的概念:

    /** This isn't participating in a sync. */
    public static final int SYNC_STATE_NONE = 0;

    /** This is currently waiting for itself to finish drawing. */
    public static final int SYNC_STATE_WAITING_FOR_DRAW = 1;

    /** This container is ready, but it might still have unfinished children. */
    public static final int SYNC_STATE_READY = 2;

    @IntDef(prefix = { "SYNC_STATE_" }, value = {
            SYNC_STATE_NONE,
            SYNC_STATE_WAITING_FOR_DRAW,
            SYNC_STATE_READY,
    })
    @interface SyncState {}

SYNC_STATE_NONE表示当前WindowContainer没有参与到一次同步操作中。

SYNC_STATE_WAITING_FOR_DRAW表示当前WindowContainer正在等待自身绘制完成。

SYNC_STATE_READY表示当前WindowContainer已经绘制完成,但是其子WindowContainer中可能还有没有完成同步的。

可以看到,在同步之前,需要将mSyncState置为SYNC_STATE_READY。如果mSyncState不是SYNC_STATE_NONE,表示当前WindowContainer仍然处于同步过程中。并且这里还调用了子容器的prepareSync方法,这一步将使得所有子容器的mSyncState都被置为SYNC_STATE_READY。

这里可能会比较疑惑,为什么直接就把WindowContainer的mSyncState的状态设置为SYNC_STATE_READY了,同步不是才刚开始?我们看到,除了WindowContainer有prepareSync方法之外,WindowState也重写了这个方法:

    @Override
    boolean prepareSync() {
        if (!super.prepareSync()) {
            return false;
        }
        // In the WindowContainer implementation we immediately mark ready
        // since a generic WindowContainer only needs to wait for its
        // children to finish and is immediately ready from its own
        // perspective but at the WindowState level we need to wait for ourselves
        // to draw even if the children draw first our don't need to sync, so we start
        // in WAITING state rather than READY.
        mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
        requestRedrawForSync();

        mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
        mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
            BLAST_TIMEOUT_DURATION);
        return true;
    }

这里的注释也可以解答我们刚刚的疑问:

在WindowContainer的实现中,我们直接将mSyncState置为SYNC_STATE_READY,因为对于一个通用的WindowContainer而言,它只需要等待它的子WindowContainer完成同步,如果它有子WindowContainer完成同步,那么对它自己而言它也就完成同步处于就绪状态了。

但是在WindowState这一层,每一个WindowState都需要等待自己绘制完成,即使子窗口先绘制完成。所以对于WindowState,是以SYNC_STATE_WAITING_FOR_DRAW状态开始而不是SYNC_STATE_READY状态。

3 将WindowContainerTransaction应用到WindowContainer

    @Override
    public int applySyncTransaction(WindowContainerTransaction t,
            IWindowContainerTransactionCallback callback) {
        ......
        try {
            synchronized (mGlobalLock) {
			   ......
                applyTransaction(t, syncId, null /*transition*/);
			   ......
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

这一步的具体内容在分析WCT的应用的时候有比较系统的介绍过了。

4 BLASTSyncTransaction#setReady标记SyncGroup已经准备就绪

    @Override
    public int applySyncTransaction(WindowContainerTransaction t,
            IWindowContainerTransactionCallback callback) {
        ......
        try {
            synchronized (mGlobalLock) {
			   ......
                applyTransaction(t, syncId, null /*transition*/);
                if (syncId >= 0) {
                    setSyncReady(syncId);
                }
                return syncId;
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

这一步发生在WindowOrganizerController#applyTransaction之后,即WindowContainerTransaction中的设置都已经应用到WindowContainer上了。

    @VisibleForTesting
    void setSyncReady(int id) {
        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id);
        mService.mWindowManager.mSyncEngine.setReady(id);
    }

    @VisibleForTesting
    void addToSyncSet(int syncId, WindowContainer wc) {
        mService.mWindowManager.mSyncEngine.addToSyncSet(syncId, wc);
    }

WindowOrganizerController#setSyncReady调用BLASTSyncEngine#setReady。

    void setReady(int id, boolean ready) {
        mActiveSyncs.get(id).setReady(ready);
    }

    void setReady(int id) {
        setReady(id, true);
    }

BLASTSyncEngine#setReady调用SyncGroup#setReady。

        private void setReady(boolean ready) {
            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready", mSyncId);
            mReady = ready;
            if (!ready) return;
            mWm.mWindowPlacerLocked.requestTraversal();
        }

将SyncGroup的成员变量mReady置为true,表示所有WindowContainerTransaction都已经应用完成,然后请求一次界面刷新。

5 等待窗口绘制完成后调用transactionReady

每次刷新界面的时候,RootWindowContainer#performSurfacePlacementNoTrace会调用BLASTSyncEngine#onSurfacePlacement。

    void onSurfacePlacement() {
        // backwards since each state can remove itself if finished
        for (int i = mActiveSyncs.size() - 1; i >= 0; --i) {
            mActiveSyncs.valueAt(i).onSurfacePlacement();
        }
    }

这里又调用了SyncGroup#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();
        }

这里会对加入到mRootMembers的WindowContainer进行遍历,mRootMembers里的数据是之前调用addToSync的时候填充的。WindowContainer#isSyncFinished返回true表示当前WindowContainer同步完成。只有当mRootMembers中所有的WindowContainer都同步完成,本次同步才算结束,才可以调用SyncGroup#finishNow。

由于这个方法在每次界面刷新的时候都会调用,所以这里的工作就是不断检查当前SyncGroup中的所有WindowContainer是否同步完成,一旦同步完成,就去调用SyncGroup#finishNow。

这里先跳过WindowContainer#isSyncFinished,看下SyncGroup#finishNow:

        private void finishNow() {
            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 */);
            }
            mListener.onTransactionReady(mSyncId, merged);
            mActiveSyncs.remove(mSyncId);
        }

SyncGroup#finishNow做了以下几件事:

5.1 取得一个存放最终结果的Transaction

            SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
            if (mOrphanTransaction != null) {
                merged.merge(mOrphanTransaction);
            }

将mOrphanTransaction合并到此Transaction中,mOrphanTransactionon从调用的地方来看,当SyncGroup中的WindowContainer的父WindowContainer发生变化时,会将该WindowContainer的mSyncTransaction合入到mOrphanTransactionon,避免该WindowContainer的mSyncTransaction保存的修改在parent更改的时候丢失。

5.2 调用WindowContainer#finishSync

    /**
     * Recursively finishes/cleans-up sync state of this subtree and collects all the sync
     * transactions into `outMergedTransaction`.
     * @param outMergedTransaction A transaction to merge all the recorded sync operations into.
     * @param cancel If true, this is being finished because it is leaving the sync group rather
     *               than due to the sync group completing.
     */
    void finishSync(Transaction outMergedTransaction, boolean cancel) {
        if (mSyncState == SYNC_STATE_NONE) return;
        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "finishSync cancel=%b for %s", cancel, this);
        outMergedTransaction.merge(mSyncTransaction);
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            mChildren.get(i).finishSync(outMergedTransaction, cancel);
        }
        mSyncState = SYNC_STATE_NONE;
        if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
        mSyncGroup = null;
    }

这里传入的5.1中创建的存放最终结果的outMergedTransaction。

1)、将当前WindowContainer自身的mSyncTransaction合入到outMergedTransaction。

2)、WindowContainer#finishSync递归调用所有子WindowContainer#finishSync,最终将WindowContainerTransaction带来的所有Transaction变化全部都合并到outMergedTransaction。

3)、将mSyncState重置为同步前的状态SYNC_STATE_NONE,mSyncGroup置空。

5.3 调用TransactionReadyListener#onTransactionReady

mListener.onTransactionReady(mSyncId, merged);

这里的TransactionReadyListener就是WindowOrganizerController,是在WindowOrganizerController调用startSyncSet创建SyncGroup的时候传入的,所以这里调用的是WindowOrganizerController#onTransactionReady:

    @Override
    public void onTransactionReady(int syncId, SurfaceControl.Transaction t) {
        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId);
        final IWindowContainerTransactionCallback callback =
                mTransactionCallbacksByPendingSyncId.get(syncId);

        try {
            callback.onTransactionReady(syncId, t);
        } catch (RemoteException e) {
            // If there's an exception when trying to send the mergedTransaction to the client, we
            // should immediately apply it here so the transactions aren't lost.
            t.apply();
        }

        mTransactionCallbacksByPendingSyncId.remove(syncId);
    }

所有参与到此次同步的WindowContainer都完成同步后,会由WindowOrganizerController根据syncId从mTransactionCallbacksByPendingSyncId中找到对应的回调对象,即SyncCallback,然后调用SyncCallback#onTransactionReady,并且传入存放了最终同步结果的Transaction。

5.4 将ID从mActiveSyncs中移除。

mActiveSyncs.remove(mSyncId);

直到这里,本次同步操作在WM端的部分才算完成了,所有configuration更改引起的Transaction变化全部都合并到了一个Transaction中,并且传给了SystemUI,后续需要SystemUI的SyncTransactionQueue那边进行apply,之前对SyncTransactionQueue的分析中也有讲过。

5.5 WindowContainer何时被视为同步完成

上面跳过了一个重要环节,在BLASTSyncEngine#onSurfacePlacement中,会对参与本次同步的所有WindowContainer调用WindowContainer#isSyncFinished判断其是否同步完成。只有SyncGroup中的所有WindowContainer都同步完成,才会调用SyncGroup#finishNow。

当时跳过了WindowContainer#isSyncFinished的分析,这一节来详细分析一下。

    /**
     * Checks if the subtree rooted at this container is finished syncing (everything is ready or
     * not visible). NOTE, this is not const: it will cancel/prepare itself depending on its state
     * in the hierarchy.
     *
     * @return {@code true} if this subtree is finished waiting for sync participants.
     */
    boolean isSyncFinished() {
        if (!isVisibleRequested()) {
            return true;
        }
        if (mSyncState == SYNC_STATE_NONE) {
            prepareSync();
        }
        if (mSyncState == SYNC_STATE_WAITING_FOR_DRAW) {
            return false;
        }
        // READY
        // Loop from top-down.
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowContainer child = mChildren.get(i);
            final boolean childFinished = child.isSyncFinished();
            if (childFinished && child.isVisibleRequested() && child.fillsParent()) {
                // Any lower children will be covered-up, so we can consider this finished.
                return true;
            }
            if (!childFinished) {
                return false;
            }
        }
        return true;
    }

判断一个WindowContainer是否完成同步,不仅当前WindowContainer的mSyncState得是SYNC_STATE_READY,它的子WindowContainer也要有完成同步的。

根据之前对WindowContaIner#prepareSync的分析,非WindowState类型的容器,在同步开始的时候都已经在WindowContainer#prepareSync中设置其mSyncState为SYNC_STATE_READY了,所以这里只需要看WindowState类型的子容器有没有就绪就好了,其它类型的WIndowContainer都不需要关注。

另外WindowState类型的容器一开始在WindowState#prepareSync中将mSyncState设置为SYNC_STATE_WAITING_FOR_DRAW,那么对于WindowState来说,isSyncFinished方法能够返回true,首要条件就是,mSyncState必须在后续被设置为SYNC_STATE_READY,这一步只能在WindowContainer#onSyncFinishedDrawing:

    /**
     * Call this when this container finishes drawing content.
     *
     * @return {@code true} if consumed (this container is part of a sync group).
     */
    boolean onSyncFinishedDrawing() {
        if (mSyncState == SYNC_STATE_NONE) return false;
        mSyncState = SYNC_STATE_READY;
        mWmService.mWindowPlacerLocked.requestTraversal();
        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "onSyncFinishedDrawing %s", this);
        return true;
    }

WindowContainer#onSyncFinishedDrawing唯一调用的地方又在WindowState#finishDrawing:

    boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) {
        if (mOrientationChangeRedrawRequestTime > 0) {
            final long duration =
                    SystemClock.elapsedRealtime() - mOrientationChangeRedrawRequestTime;
            Slog.i(TAG, "finishDrawing of orientation change: " + this + " " + duration + "ms");
            mOrientationChangeRedrawRequestTime = 0;
        } else if (mActivityRecord != null && mActivityRecord.mRelaunchStartTime != 0
                && mActivityRecord.findMainWindow() == this) {
            final long duration =
                    SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime;
            Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
            mActivityRecord.mRelaunchStartTime = 0;
        }

        executeDrawHandlers(postDrawTransaction);
        if (!onSyncFinishedDrawing()) {
            return mWinAnimator.finishDrawingLocked(postDrawTransaction);
        }

        if (postDrawTransaction != null) {
            mSyncTransaction.merge(postDrawTransaction);
        }

        mWinAnimator.finishDrawingLocked(null);
        // We always want to force a traversal after a finish draw for blast sync.
        return true;
    }

这个方法唯一的调用栈是从APP端调过来的:

ViewRootImpl#reportedDrawFinished

-> Session#finishDrawing

	(跨进程)

	-> WMS#finishDrawingWindow

		-> WindowState#finishDrawing

			-> WindowContainer#onSyncFinishedDrawing

那么也就是说,只有APP端完成了界面绘制并且通知到了WM端,当前WindowState才能设置其mSyncState为SYNC_STATE_READY。

然而这个还是条件之一,判断一个WindowContainer是否完成同步,还有其他限制:

        // READY
        // Loop from top-down.
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final WindowContainer child = mChildren.get(i);
            final boolean childFinished = child.isSyncFinished();
            if (childFinished && child.isVisibleRequested() && child.fillsParent()) {
                // Any lower children will be covered-up, so we can consider this finished.
                return true;
            }
            if (!childFinished) {
                return false;
            }
        }
        return true;

1)、如果我们能先找到一个已经完成绘制的子窗口,并且该子窗口后续要显示,且能够覆盖的了它下面的所有子窗口,那么可以认为当前WindowContainer绘制完成。

2)、如果我们找不到一个满足条件1的子窗口,但是所有窗口都已经绘制完成,那么也可以认为当前WindowContainer绘制完成;

3)、如果我们在找到一个满足条件1的子窗口之前,先找到一个没有完成绘制的子窗口,那么就认为当前WindowContainer没有绘制完成。

三、总结

1 BLASTSyncEngine机制流程

1)、在WindowOrganizerController#applyTransaction之前,调用BLASTSyncEngine#startSyncSet,创建一个拥有唯一ID的SyncGroup对象:

WindowOrganizerController#startSyncWithOrganizer

-> BLASTSyncEngine#startSyncSet

2)、在WindowContainerTransaction中的设置应用到各个WindowContainer之前,调用BLASTSyncEngine#addToSyncSet将这些要应用WindowContainerTransaction的WindowContainer加入到SyncGroup中,并且调用WindowContainer#prepareSync为这些WindowContainer设置初始状态:

WindowOrganizerController#addToSyncSet

-> BLASTSyncEngine#addToSyncSet 

	-> SyncGroup#addToSync 

		-> WindowContainer#prepareSync

3)、所有参与同步的WindowContainer应用WindowContainerTransaction。

4)、所有参与同步的WindowContainer应用WindowContainerTransaction之后,通知BLASTSyncEngineSyncGroup已经准备就绪,可以开始请求绘制。

WindowOrganizerController#setSyncReady

-> BLASTSyncEngine#setReady 

	-> BLASTSyncEngine#setReady 

		-> SyncGroup#setReady

随后在每次界面刷新时,都会调用BLASTSyncEngine#onSurfacePlacement,不断去检查本次SyncGroup中的所有WindowContainer是否已经全部完成同步:

RootWindowContainer#performSurfacePlacementNoTrace

-> BLASTSyncEngine#onSurfacePlacement 

	-> SyncGroup#onSurfacePlacement

5)、一旦所有的容器都已经同步完成,那么调用WindowContainer#finishSync将所有参与同步WindowContainer的有关于Transaction改动,即WindowContainer的成员变量mSyncTransaction和SyncGroup的成员变量mOrphanTransaction,合入到一个Transaction中,再将该Transaction传给WindowOrganizerController,最终发送给客户端(这里就是SyncCallback):

SyncGroup#onSurfacePlacement

-> SyncGroup#finishNow 

	-> WindowOrganizerController#onTransactionReady 

		(跨进程)

		-> SyncCallback#onTransactionReady

2 WindowContainer同步状态mSyncState

有三种状态:SYNC_STATE_NONE、SYNC_STATE_WAITING_FOR_DRAW和SYNC_STATE_READY,看下都是在什么时候设置的。

1)、同步开始,所有非WindowState类型的WindowContainer的mSyncState会被设置为SYNC_STATE_READY:

    /**
     * Prepares this container for participation in a sync-group. This includes preparing all its
     * children.
     *
     * @return {@code true} if something changed (eg. this wasn't already in the sync group).
     */
    boolean prepareSync() {
        if (mSyncState != SYNC_STATE_NONE) {
            // Already part of sync
            return false;
        }
        for (int i = getChildCount() - 1; i >= 0; --i) {
            final WindowContainer child = getChildAt(i);
            child.prepareSync();
        }
        mSyncState = SYNC_STATE_READY;
        return true;
    }

WindowState类型的容器mSyncState会被设置为SYNC_STATE_WAITING_FOR_DRAW:

    @Override
    boolean prepareSync() {
        if (!super.prepareSync()) {
            return false;
        }
        // In the WindowContainer implementation we immediately mark ready
        // since a generic WindowContainer only needs to wait for its
        // children to finish and is immediately ready from its own
        // perspective but at the WindowState level we need to wait for ourselves
        // to draw even if the children draw first our don't need to sync, so we start
        // in WAITING state rather than READY.
        mSyncState = SYNC_STATE_WAITING_FOR_DRAW;
        requestRedrawForSync();

        mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
        mWmService.mH.sendNewMessageDelayed(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this,
            BLAST_TIMEOUT_DURATION);
        return true;
    }

2)、窗口绘制完成,对于WindowState,mSyncState被设置为SYNC_STATE_READY:

    /**
     * Call this when this container finishes drawing content.
     *
     * @return {@code true} if consumed (this container is part of a sync group).
     */
    boolean onSyncFinishedDrawing() {
        if (mSyncState == SYNC_STATE_NONE) return false;
        mSyncState = SYNC_STATE_READY;
        mWmService.mWindowPlacerLocked.requestTraversal();
        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "onSyncFinishedDrawing %s", this);
        return true;
    }

3)、同步结束,所有WindowContainer的mSyncState被重置为SYNC_STATE_NONE:

    /**
     * Recursively finishes/cleans-up sync state of this subtree and collects all the sync
     * transactions into `outMergedTransaction`.
     * @param outMergedTransaction A transaction to merge all the recorded sync operations into.
     * @param cancel If true, this is being finished because it is leaving the sync group rather
     *               than due to the sync group completing.
     */
    void finishSync(Transaction outMergedTransaction, boolean cancel) {
        if (mSyncState == SYNC_STATE_NONE) return;
        ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "finishSync cancel=%b for %s", cancel, this);
        outMergedTransaction.merge(mSyncTransaction);
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            mChildren.get(i).finishSync(outMergedTransaction, cancel);
        }
        mSyncState = SYNC_STATE_NONE;
        if (cancel && mSyncGroup != null) mSyncGroup.onCancelSync(this);
        mSyncGroup = null;
    }

3 同步完成的本质

要看一个WindowContainer(一般是Task)是否同步完成,其实是看它包含的ActivityRecord对应的窗口是否绘制完成:

ViewRootImpl#reportedDrawFinished

-> Session#finishDrawing

	(跨进程)

	-> WMS#finishDrawingWindow

		-> WindowState#finishDrawing

			-> WindowContainer#onSyncFinishedDrawing

这个依赖于APP端界面的绘制情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值