performSurfacePlacementNoTrace()
这一段从performSurfacePlacement()开始讲起,因为在trace中可以看到在SystemServer中,动效会从performSurfacePlacement这个tag点触发。这里的流程就是在窗口状态改变之后,会触发performSurfacePlacement流程,在performSurfacePlacement流程中会进行一系列的检查,从而决定是开启动效流程,或者是继续等待,直到动效的前置条件已经就绪再开启动效,具体流程就在这里展开:
void performSurfacePlacement() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
try {
performSurfacePlacementNoTrace();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
onSurfacePlacement()
然后在performSurfacePlacementNoTrace()中会触发BLASTSyncEngine中的onSurfacePlacement()方法。
void onSurfacePlacement() {
if (mActiveSyncs.isEmpty()) return; (1)
// queue in-order since we want interdependent syncs to become ready in the same order they
// started in.
mTmpFinishQueue.addAll(mActiveSyncs);
// There shouldn't be any dependency cycles or duplicates, but add an upper-bound just
// in case. Assuming absolute worst case, each visit will try and revisit everything
// before it, so n + (n-1) + (n-2) ... = (n+1)*n/2
int visitBounds = ((mActiveSyncs.size() + 1) * mActiveSyncs.size()) / 2; //(2)
while (!mTmpFinishQueue.isEmpty()) {
if (visitBounds <= 0) {
Slog.e(TAG, "Trying to finish more syncs than theoretically possible. This "
+ "should never happen. Most likely a dependency cycle wasn't detected.");
}
--visitBounds;
final SyncGroup group = mTmpFinishQueue.remove(0); //(3)
final int grpIdx = mActiveSyncs.indexOf(group); //(4)
// Skip if it's already finished:
if (grpIdx < 0) continue;
if (!group.tryFinish()) continue; //(5)
// Finished, so update dependencies of any prior groups and retry if unblocked.
int insertAt = 0;
for (int i = 0; i < mActiveSyncs.size(); ++i) { //(6)
final SyncGroup active = mActiveSyncs.get(i); //(7)
if (!active.mDependencies.remove(group)) continue; //(8)
// Anything afterwards is already in queue.
if (i >= grpIdx) continue; //(9)
if (!active.mDependencies.isEmpty()) continue; //(10)
// `active` became unblocked so it can finish, since it started earlier, it should
// be checked next to maintain order.
mTmpFinishQueue.add(insertAt, mActiveSyncs.get(i)); //(11)
insertAt += 1;
}
}
}
在onSurfacePlacement()方法中,首先在(1)处检查的是mActiveSyncs是否为空。在代码中找到mActiveSyncs的声明,发现其声明如下,所以对其操作都在该文件内。在本文件中只找到唯一一处向其中添加对象的地方就在startSyncSet方法中。这里可以接上篇博客启动动效流程梳理(一),其中在Transition.java中将mState置为STATE_COLLECTING时,会触发该操作,所以一般从前面的流程走过来的话,这里的mActiveSyncs一般是不为空的。
private final ArrayList<SyncGroup> mActiveSyncs = new ArrayList<>();
void startSyncSet(SyncGroup s, long timeoutMs, boolean parallel) {
final boolean alreadyRunning = mActiveSyncs.size() > 0;
if (!parallel && alreadyRunning) {
// We only support overlapping syncs when explicitly declared `parallel`.
Slog.e(TAG, "SyncGroup " + s.mSyncId
+ ": Started when there is other active SyncGroup");
}
mActiveSyncs.add(s);
// For now, parallel implies this.
s.mIgnoreIndirectMembers = parallel;
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Started %sfor listener: %s",
s.mSyncId, (parallel && alreadyRunning ? "(in parallel) " : ""), s.mListener);
scheduleTimeout(s, timeoutMs);
}
在判断完mActiveSyncs是否为空之后,会将其移入mTmpFinishQueue中,然后再将元素从mTmpFinishQueue中依次从头移除,即(3)处所示,然后再在mActiveSyncs中找到该元素对应的索引,正常情况下grpIdx是大于等于0的,因为可以看到mTmpFinishQueue就是mActiveSyncs拷贝过来的,但是在(5)处会进入到tryFinish的流程中,这里可能会移除mActiveSyncs中的group对象,所以不排除这种可能,所以这里的注释就是这个意思。
TryFinish()
然后先进(5)的tryFinish()看下做了什么:
private boolean tryFinish() {
if (!mReady) return false;
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: onSurfacePlacement checking %s",
mSyncId, mRootMembers);
if (!mDependencies.isEmpty()) { //(12)
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Unfinished dependencies: %s",
mSyncId, mDependencies);
return false;
}
for (int i = mRootMembers.size() - 1; i >= 0; --i) { //(13)
final WindowContainer wc = mRootMembers.valueAt(i);
if (!wc.isSyncFinished(this)) {
ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Unfinished container: %s",
mSyncId, wc);
return false;
}
}
finishNow(); //(14)
return true;
}
首先是检查mReady标志位,这里只有在setReady处才会改变mReady的属性,所以也就是说,只有执行setReady(true)后才会执行接下来的流程。然后会检查当前的SyncGroup的mDependencies中的SyncGroup是否为空,而mDependencies的注解是必须在当前SyncGroup之前finish的SyncGroup队列。 根据(12)处的注释解读,mDependencies中的成员,会在finish后被移除队列,所以如果一个SyncGroup在tryFinish时,其mDependencies不为空,就会直接返回失败。 再到(13)处的for循环,这里回去检查当前的SyncGroup的mRootMembers中的各个成员是否绘制完成,如果存在未绘制完成的WindowContainer,则也直接返回false。这里的绘制完成是通过finishDrawing触发,finishDrawing会在应用侧绘制完成时触发,这块前面博客启动动效流程梳理(一)中也讲到过。
FinishNow()
做完上述两个检查才会继续执行到finishNow流程中。finishNow流程的话,简单讲下,首先是创建一个Transaction对象merged,然后再将子节点mRootMembers的mSyncTransaction都合并进merged中。再去执行mRootMembers的mChildren的finishSync方法,这里就看WindowContainer的mChildren是什么类型了,反正基本操作就是将mSyncState置为SYNC_STATE_NONE,且mSyncGroup置为null。之后再将mRootMembers中各个成员及其成员的mChildren都放入wcAwaitingCommit之中。
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, this, 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(SurfaceControl.Transaction t) {
synchronized (mWm.mGlobalLock) {
if (ran) {
return;
}
mHandler.removeCallbacks(this);
ran = true;
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);
synchronized (mWm.mGlobalLock) {
onCommitted(merged.mNativeObject != 0
? merged : mWm.mTransactionFactory.get());
}
}
};
CommitCallback callback = new CommitCallback();
merged.addTransactionCommittedListener(Runnable::run,
() -> callback.onCommitted(new SurfaceControl.Transaction())); //(15)
mHandler.postDelayed(callback, BLAST_TIMEOUT_DURATION); //(16)
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady");
mListener.onTransactionReady(mSyncId, merged);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
mActiveSyncs.remove(this);
mHandler.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.
mHandler.post(() -> {
synchronized (mWm.mGlobalLock) {
pt.mApplySync.run();
}
});
}
// Notify idle listeners
for (int i = mOnIdleListeners.size() - 1; i >= 0; --i) {
// If an idle listener adds a sync, though, then stop notifying.
if (mActiveSyncs.size() > 0) break;
mOnIdleListeners.get(i).run();
}
}
在finishNow方法中定义了一个CommitCallback类,其实现了Runnable接口。CommitCallback类中有两个方法,run和onCommitted,主要就是onCommitted方法,这个方法会获取WindowManagerService的大锁,然后遍历wcAwaitingCommit中的成员,并将其mSyncTransaction合并到merged中,然后将merged进行apply()。而callback的onCommitted方法则会在(15)处,于Transaction被从应用侧apply到SF侧的同时调用,这里的机制可以看到上一篇博客WCT系列(五):addTransactionCommittedListener。
在之后就是mListener.onTransactionReady(mSyncId, merged)该方法的调用,这里就是回去播放动效了。该方法需要跳到wm\Transition.java中查看,简单的归纳下该方法的具体作用:
1、 将mState状态修改为STATE_PLAYING;并设置mStartTransaction和mFinishTransaction的状态;
2、 通过动效的mParticipants和mChanges参数计算出mTargets参数;
3、 计算出TransitionInfo类型的info参数的值;
4、 将需要播放的Transition对象从mCollectingTransition列表中移到mPlayingTransition列表中;
5、 发送onTransitionReady Binder到shell线程;