窗口动画流程
我把窗口动画的执行流程大概分为以下几步
触发添加窗口动画
加载对应的动画资源
创建leash,同时把执行动画的窗口容器reparent到 leash上
开始循环执行窗口动画
动画结束,把执行动画的窗口重新reparent到该容器原来的父节点上。
WindowState
frameworks/base/services/core/java/com/android/server/wm/WindowState.java
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
InsetsControlTarget, InputTarget {
final WindowStateAnimator mWinAnimator;//窗口动画
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow) {
this(service, s, c, token, parentWindow, appOp, a, viewVisibility, ownerId, showUserId,
ownerCanAddInternalSystemWindow, new PowerManagerWrapper() {
@Override
public void wakeUp(long time, @WakeReason int reason, String details) {
service.mPowerManager.wakeUp(time, reason, details);
}
@Override
public boolean isInteractive() {
return service.mPowerManager.isInteractive();
}
});
}
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
PowerManagerWrapper powerManagerWrapper) {
super(service);
mTmpTransaction = service.mTransactionFactory.get();
mSession = s;//会话
mClient = c;//客户端可以通过此对象回传消息
mAppOp = appOp;
mToken = token;//令牌
mActivityRecord = mToken.asActivityRecord();//窗口对应的Activity
mOwnerUid = ownerId;
mShowUserId = showUserId;
mOwnerCanAddInternalSystemWindow = ownerCanAddInternalSystemWindow;
mWindowId = new WindowId(this);
mAttrs.copyFrom(a);//拷贝窗口属性
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
mViewVisibility = viewVisibility;
mPolicy = mWmService.mPolicy;//窗口策略对象
mContext = mWmService.mContext;
DeathRecipient deathRecipient = new DeathRecipient();
mPowerManagerWrapper = powerManagerWrapper;
mForceSeamlesslyRotate = token.mRoundedCornerOverlay;
mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle(
mActivityRecord != null
? mActivityRecord.getInputApplicationHandle(false /* update */) : null,
getDisplayId()));
mInputWindowHandle.setOwnerPid(s.mPid);
mInputWindowHandle.setOwnerUid(s.mUid);
mInputWindowHandle.setName(getName());
mInputWindowHandle.setPackageName(mAttrs.packageName);
mInputWindowHandle.setLayoutParamsType(mAttrs.type);
// Check private trusted overlay flag and window type to set trustedOverlay variable of
// input window handle.
mInputWindowHandle.setTrustedOverlay(
((mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
&& mOwnerCanAddInternalSystemWindow)
|| InputMonitor.isTrustedOverlay(mAttrs.type));
if (DEBUG) {
Slog.v(TAG, "Window " + this + " client=" + c.asBinder()
+ " token=" + token + " (" + mAttrs.token + ")" + " params=" + a);
}
try {
c.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
mDeathRecipient = null;
mIsChildWindow = false;
mLayoutAttached = false;
mIsImWindow = false;
mIsWallpaper = false;
mIsFloatingLayer = false;
mBaseLayer = 0;
mSubLayer = 0;
mWinAnimator = null;
mWpcForDisplayAreaConfigChanges = null;
return;
}
mDeathRecipient = deathRecipient;//窗口消亡回调对象,可以根据此对象是否为空则来判断窗口是否已经死亡
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
mIsChildWindow = true;
mLayoutAttached = mAttrs.type !=
WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
mIsImWindow = parentWindow.mAttrs.type == TYPE_INPUT_METHOD
|| parentWindow.mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = parentWindow.mAttrs.type == TYPE_WALLPAPER;
} else {
// The multiplier here is to reserve space for multiple
// windows in the same type layer.
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
mSubLayer = 0;
mIsChildWindow = false;
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
}
mIsFloatingLayer = mIsImWindow || mIsWallpaper;
if (mActivityRecord != null && mActivityRecord.mShowForAllUsers) {
// Windows for apps that can show for all users should also show when the device is
// locked.
mAttrs.flags |= FLAG_SHOW_WHEN_LOCKED;
}
mWinAnimator = new WindowStateAnimator(this);//窗口状态动画
mWinAnimator.mAlpha = a.alpha;//动画透明度
mRequestedWidth = 0;
mRequestedHeight = 0;
mLastRequestedWidth = 0;
mLastRequestedHeight = 0;
mLayer = 0;
mOverrideScale = mWmService.mAtmService.mCompatModePackages.getCompatScale(
mAttrs.packageName, s.mUid);
// Make sure we initial all fields before adding to parentWindow, to prevent exception
// during onDisplayChanged.
if (mIsChildWindow) {
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", this, parentWindow);
parentWindow.addChild(this, sWindowSubLayerComparator);
}
// System process or invalid process cannot register to display area config change.
mWpcForDisplayAreaConfigChanges = (s.mPid == MY_PID || s.mPid < 0)
? null
: service.mAtmService.getProcessController(s.mPid, s.mUid);
}
}
触发添加窗口动画WindowStateAnimator类
在WMS中最重要的函数performSurfacePlacementNoTrace()后期,会遍历一块屏幕上所有的窗口(WindowState),如果该WindowState有surfece的话,会调用到WindowState.winAnimator.ommitFinishDrawingLocked()方法:
base/services/core/java/com/android/server/wm/WindowStateAnimator.java
class WindowStateAnimator {
static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM;
static final int PRESERVED_SURFACE_LAYER = 1;
static final int ROOT_TASK_CLIP_AFTER_ANIM = 0;
static final int ROOT_TASK_CLIP_NONE = 1;
final WindowManagerService mService;
final WindowState mWin;
final WindowAnimator mAnimator;
final Session mSession;
final WindowManagerPolicy mPolicy;
final Context mContext;
final boolean mIsWallpaper;
private final WallpaperController mWallpaperControllerLocked;
boolean mAnimationIsEntrance;
WindowSurfaceController mSurfaceController;
float mShownAlpha = 0;
float mAlpha = 0;
float mLastAlpha = 0;
/**
* This is rectangle of the window's surface that is not covered by
* system decorations.
*/
private final Rect mSystemDecorRect = new Rect();
// Set to true if, when the window gets displayed, it should perform
// an enter animation.
boolean mEnterAnimationPending;
/** Used to indicate that this window is undergoing an enter animation. Used for system
* windows to make the callback to View.dispatchOnWindowShownCallback(). Set when the
* window is first added or shown, cleared when the callback has been made. */
boolean mEnteringAnimation;
/** The pixel format of the underlying SurfaceControl */
int mSurfaceFormat;
/** This is set when there is no Surface */
static final int NO_SURFACE = 0;
/** This is set after the Surface has been created but before the window has been drawn. During
* this time the surface is hidden. */
static final int DRAW_PENDING = 1;
/** This is set after the window has finished drawing for the first time but before its surface
* is shown. The surface will be displayed when the next layout is run. */
static final int COMMIT_DRAW_PENDING = 2;
/** This is set during the time after the window's drawing has been committed, and before its
* surface is actually shown. It is used to delay showing the surface until all windows in a
* token are ready to be shown. */
static final int READY_TO_SHOW = 3;
/** Set when the window has been shown in the screen the first time. */
static final int HAS_DRAWN = 4;
String drawStateToString() {
switch (mDrawState) {
case NO_SURFACE: return "NO_SURFACE";
case DRAW_PENDING: return "DRAW_PENDING";
case COMMIT_DRAW_PENDING: return "COMMIT_DRAW_PENDING";
case READY_TO_SHOW: return "READY_TO_SHOW";
case HAS_DRAWN: return "HAS_DRAWN";
default: return Integer.toString(mDrawState);
}
}
int mDrawState;
/** Was this window last hidden? */
boolean mLastHidden;
int mAttrType;
private final Rect mTmpSize = new Rect();
private final SurfaceControl.Transaction mPostDrawTransaction =
new SurfaceControl.Transaction();
WindowStateAnimator(final WindowState win) {
final WindowManagerService service = win.mWmService;
mService = service;
mAnimator = service.mAnimator;
mPolicy = service.mPolicy;
mContext = service.mContext;
mWin = win;
mSession = win.mSession;
mAttrType = win.mAttrs.type;
mIsWallpaper = win.mIsWallpaper;
mWallpaperControllerLocked = win.getDisplayContent().mWallpaperController;
}
void onAnimationFinished() {
// Done animating, clean up.
if (DEBUG_ANIM) Slog.v(
TAG, "Animation done in " + this + ": exiting=" + mWin.mAnimatingExit
+ ", reportedVisible="
+ (mWin.mActivityRecord != null && mWin.mActivityRecord.reportedVisible));
mWin.checkPolicyVisibilityChange();
final DisplayContent displayContent = mWin.getDisplayContent();
if ((mAttrType == LayoutParams.TYPE_STATUS_BAR
|| mAttrType == LayoutParams.TYPE_NOTIFICATION_SHADE) && mWin.isVisibleByPolicy()) {
// Upon completion of a not-visible to visible status bar animation a relayout is
// required.
displayContent.setLayoutNeeded();
}
mWin.onExitAnimationDone();
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
if (displayContent.mWallpaperController.isWallpaperTarget(mWin)) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
if (DEBUG_LAYOUT_REPEATS) {
mService.mWindowPlacerLocked.debugLayoutRepeats(
"WindowStateAnimator", displayContent.pendingLayoutChanges);
}
if (mWin.mActivityRecord != null) {
mWin.mActivityRecord.updateReportedVisibilityLocked();
}
}
void hide(SurfaceControl.Transaction transaction, String reason) {
if (!mLastHidden) {
//dump();
mLastHidden = true;
if (mSurfaceController != null) {
mSurfaceController.hide(transaction, reason);
}
}
}
boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction,
boolean forceApplyNow) {
final boolean startingWindow =
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
if (startingWindow) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finishing drawing window %s: mDrawState=%s",
mWin, drawStateToString());
}
boolean layoutNeeded = false;
if (mDrawState == DRAW_PENDING) {
ProtoLog.v(WM_DEBUG_DRAW,
"finishDrawingLocked: mDrawState=COMMIT_DRAW_PENDING %s in %s", mWin,
mSurfaceController);
if (startingWindow) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Draw state now committed in %s", mWin);
}
mDrawState = COMMIT_DRAW_PENDING;
layoutNeeded = true;
}
if (postDrawTransaction != null) {
// If there is no surface, the last draw was for the previous surface. We don't want to
// wait until the new surface is shown and instead just apply the transaction right
// away.
if (mLastHidden && mDrawState != NO_SURFACE && !forceApplyNow) {
mPostDrawTransaction.merge(postDrawTransaction);
} else {
mWin.getSyncTransaction().merge(postDrawTransaction);
}
layoutNeeded = true;
}
return layoutNeeded;
}
// This must be called while inside a transaction.
boolean commitFinishDrawingLocked() {
if (DEBUG_STARTING_WINDOW_VERBOSE &&
mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) {
Slog.i(TAG, "commitFinishDrawingLocked: " + mWin + " cur mDrawState="
+ drawStateToString());
}
if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
return false;
}
if (DEBUG_ANIM) {
Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceController);
}
mDrawState = READY_TO_SHOW;
boolean result = false;
final ActivityRecord activity = mWin.mActivityRecord;
if (activity == null || activity.canShowWindows()
|| mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
result = mWin.performShowLocked();
}
return result;
}
void resetDrawState() {
mDrawState = DRAW_PENDING;
if (mWin.mActivityRecord == null) {
return;
}
if (!mWin.mActivityRecord.isAnimating(TRANSITION)) {
mWin.mActivityRecord.clearAllDrawn();
}
}
}
WindowAnimator类
base/services/core/java/com/android/server/wm/WindowAnimator.java
public class WindowAnimator {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowAnimator" : TAG_WM;
final WindowManagerService mService;
final Context mContext;
final WindowManagerPolicy mPolicy;
/** Is any window animating? */
private boolean mLastRootAnimating;
/** True if we are running any animations that require expensive composition. */
private boolean mRunningExpensiveAnimations;
final Choreographer.FrameCallback mAnimationFrameCallback;
/** Time of current animation step. Reset on each iteration */
long mCurrentTime;
int mBulkUpdateParams = 0;
Object mLastWindowFreezeSource;
SparseArray<DisplayContentsAnimator> mDisplayContentsAnimators = new SparseArray<>(2);
private boolean mInitialized = false;
// When set to true the animator will go over all windows after an animation frame is posted and
// check if some got replaced and can be removed.
private boolean mRemoveReplacedWindows = false;
private Choreographer mChoreographer;
/**
* Indicates whether we have an animation frame callback scheduled, which will happen at
* vsync-app and then schedule the animation tick at the right time (vsync-sf).
*/
private boolean mAnimationFrameCallbackScheduled;
boolean mNotifyWhenNoAnimation = false;
/**
* A list of runnable that need to be run after {@link WindowContainer#prepareSurfaces} is
* executed and the corresponding transaction is closed and applied.
*/
private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
private boolean mInExecuteAfterPrepareSurfacesRunnables;
private final SurfaceControl.Transaction mTransaction;
WindowAnimator(final WindowManagerService service) {
mService = service;
mContext = service.mContext;
mPolicy = service.mPolicy;
mTransaction = service.mTransactionFactory.get();
service.mAnimationHandler.runWithScissors(
() -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
mAnimationFrameCallback = frameTimeNs -> {
synchronized (mService.mGlobalLock) {
mAnimationFrameCallbackScheduled = false;
final long vsyncId = mChoreographer.getVsyncId();
animate(frameTimeNs, vsyncId);
if (mNotifyWhenNoAnimation && !mLastRootAnimating) {
mService.mGlobalLock.notifyAll();
}
}
};
}
void addDisplayLocked(final int displayId) {
// Create the DisplayContentsAnimator object by retrieving it if the associated
// {@link DisplayContent} exists.
getDisplayContentsAnimatorLocked(displayId);
}
void removeDisplayLocked(final int displayId) {
mDisplayContentsAnimators.delete(displayId);
}
void ready() {
mInitialized = true;
}
private void animate(long frameTimeNs, long vsyncId) {
if (!mInitialized) {
return;
}
// Schedule next frame already such that back-pressure happens continuously.
scheduleAnimation();
final RootWindowContainer root = mService.mRoot;
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
mBulkUpdateParams = 0;
root.mOrientationChangeComplete = true;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
mService.openSurfaceTransaction();
try {
// Remove all deferred displays, tasks, and activities.
root.handleCompleteDeferredRemoval();
final AccessibilityController accessibilityController =
mService.mAccessibilityController;
final int numDisplays = mDisplayContentsAnimators.size();
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = root.getDisplayContent(displayId);
// Update animations of all applications, including those associated with
// exiting/removed apps.
dc.updateWindowsForAnimator();
dc.prepareSurfaces();
}
for (int i = 0; i < numDisplays; i++) {
final int displayId = mDisplayContentsAnimators.keyAt(i);
final DisplayContent dc = root.getDisplayContent(displayId);
dc.checkAppWindowsReadyToShow();
if (accessibilityController.hasCallbacks()) {
accessibilityController.drawMagnifiedRegionBorderIfNeeded(displayId,
mTransaction);
}
}
cancelAnimation();
if (mService.mWatermark != null) {
mService.mWatermark.drawIfNeeded();
}
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
}
final boolean hasPendingLayoutChanges = root.hasPendingLayoutChanges(this);
final boolean doRequest = (mBulkUpdateParams != 0 || root.mOrientationChangeComplete)
&& root.copyAnimToLayoutParams();
if (hasPendingLayoutChanges || doRequest) {
mService.mWindowPlacerLocked.requestTraversal();
}
final boolean rootAnimating = root.isAnimating(TRANSITION | CHILDREN /* flags */,
ANIMATION_TYPE_ALL /* typesToCheck */);
if (rootAnimating && !mLastRootAnimating) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
}
if (!rootAnimating && mLastRootAnimating) {
mService.mWindowPlacerLocked.requestTraversal();
Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER, "animating", 0);
}
mLastRootAnimating = rootAnimating;
final boolean runningExpensiveAnimations =
root.isAnimating(TRANSITION | CHILDREN /* flags */,
ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_SCREEN_ROTATION
| ANIMATION_TYPE_RECENTS /* typesToCheck */);
if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
// Usually app transitions put quite a load onto the system already (with all the things
// happening in app), so pause task snapshot persisting to not increase the load.
mService.mTaskSnapshotController.setPersisterPaused(true);
mTransaction.setEarlyWakeupStart();
} else if (!runningExpensiveAnimations && mRunningExpensiveAnimations) {
mService.mTaskSnapshotController.setPersisterPaused(false);
mTransaction.setEarlyWakeupEnd();
}
mRunningExpensiveAnimations = runningExpensiveAnimations;
SurfaceControl.mergeToGlobalTransaction(mTransaction);
mService.closeSurfaceTransaction("WindowAnimator");
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
if (mRemoveReplacedWindows) {
root.removeReplacedWindows();
mRemoveReplacedWindows = false;
}
mService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
executeAfterPrepareSurfacesRunnables();
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: exit"
+ " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams)
+ " hasPendingLayoutChanges=" + hasPendingLayoutChanges);
}
}
private static String bulkUpdateParamsToString(int bulkUpdateParams) {
StringBuilder builder = new StringBuilder(128);
if ((bulkUpdateParams & WindowSurfacePlacer.SET_UPDATE_ROTATION) != 0) {
builder.append(" UPDATE_ROTATION");
}
if ((bulkUpdateParams & WindowSurfacePlacer.SET_WALLPAPER_ACTION_PENDING) != 0) {
builder.append(" SET_WALLPAPER_ACTION_PENDING");
}
return builder.toString();
}
public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
final String subPrefix = " " + prefix;
for (int i = 0; i < mDisplayContentsAnimators.size(); i++) {
pw.print(prefix); pw.print("DisplayContentsAnimator #");
pw.print(mDisplayContentsAnimators.keyAt(i));
pw.println(":");
final DisplayContent dc =
mService.mRoot.getDisplayContent(mDisplayContentsAnimators.keyAt(i));
dc.dumpWindowAnimators(pw, subPrefix);
pw.println();
}
pw.println();
if (dumpAll) {
pw.print(prefix); pw.print("mCurrentTime=");
pw.println(TimeUtils.formatUptime(mCurrentTime));
}
if (mBulkUpdateParams != 0) {
pw.print(prefix); pw.print("mBulkUpdateParams=0x");
pw.print(Integer.toHexString(mBulkUpdateParams));
pw.println(bulkUpdateParamsToString(mBulkUpdateParams));
}
}
private DisplayContentsAnimator getDisplayContentsAnimatorLocked(int displayId) {
if (displayId < 0) {
return null;
}
DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.get(displayId);
// It is possible that this underlying {@link DisplayContent} has been removed. In this
// case, we do not want to create an animator associated with it as {link #animate} will
// fail.
if (displayAnimator == null && mService.mRoot.getDisplayContent(displayId) != null) {
displayAnimator = new DisplayContentsAnimator();
mDisplayContentsAnimators.put(displayId, displayAnimator);
}
return displayAnimator;
}
void requestRemovalOfReplacedWindows(WindowState win) {
mRemoveReplacedWindows = true;
}
void scheduleAnimation() {
if (!mAnimationFrameCallbackScheduled) {
mAnimationFrameCallbackScheduled = true;
mChoreographer.postFrameCallback(mAnimationFrameCallback);
}
}
private void cancelAnimation() {
if (mAnimationFrameCallbackScheduled) {
mAnimationFrameCallbackScheduled = false;
mChoreographer.removeFrameCallback(mAnimationFrameCallback);
}
}
private class DisplayContentsAnimator {
}
boolean isAnimationScheduled() {
return mAnimationFrameCallbackScheduled;
}
Choreographer getChoreographer() {
return mChoreographer;
}
/**
* Adds a runnable to be executed after {@link WindowContainer#prepareSurfaces} is called and
* the corresponding transaction is closed and applied.
*/
void addAfterPrepareSurfacesRunnable(Runnable r) {
// If runnables are already being handled in executeAfterPrepareSurfacesRunnable, then just
// immediately execute the runnable passed in.
if (mInExecuteAfterPrepareSurfacesRunnables) {
r.run();
return;
}
mAfterPrepareSurfacesRunnables.add(r);
scheduleAnimation();
}
void executeAfterPrepareSurfacesRunnables() {
// Don't even think about to start recursing!
if (mInExecuteAfterPrepareSurfacesRunnables) {
return;
}
mInExecuteAfterPrepareSurfacesRunnables = true;
// Traverse in order they were added.
final int size = mAfterPrepareSurfacesRunnables.size();
for (int i = 0; i < size; i++) {
mAfterPrepareSurfacesRunnables.get(i).run();
}
mAfterPrepareSurfacesRunnables.clear();
mInExecuteAfterPrepareSurfacesRunnables = false;
}
}