上一篇文章分析了启动窗口的移除,它主要是做了一个 View 动画,但是,并没有从真正意义上移除/隐藏启动窗口 surface,这就导致真窗还无法显示出来。
另外,WM-Core 在移除启动窗口时,对真窗 WindowState 执行了一个 local animation,其目的仅仅是为了获得真窗 leash。然而这个动画还有一个副作用,它没法结束。这就导致真窗 leash 无法被移除。
因此,上一篇文章并没有从真正意义上完成启动窗口的移除,它的结局只是隐藏并移除启动窗口根 View,如下
// SplashscreenWindowCreator.java
// decorView 启动窗口 root view
// hideView 为 true
private void removeWindowInner(@NonNull View decorView, boolean hideView) {
// ...
if (hideView) {
// 1. 隐藏启动窗口 root view
decorView.setVisibility(View.GONE);
}
// 2. 移除启动窗口 root view
mWindowManagerGlobal.removeView(decorView, false /* immediate */);
}
本文来分析这两步,看看如何完成启动窗口 surface 的移除,并且移除真窗 leash。本文有一点点小复杂,好好看,好好学。
隐藏启动窗口 root view
// View.java
// visibility 为 GONE
public void setVisibility(@Visibility int visibility) {
setFlags(visibility, VISIBILITY_MASK);
}
void setFlags(int flags, int mask) {
// ...
int old = mViewFlags;
// 更新 mViewFlags
mViewFlags = (mViewFlags & ~mask) | (flags & mask);
// 检测哪些 flags 改变了
int changed = mViewFlags ^ old;
if (changed == 0) {
return;
}
// ...
/* Check if the GONE bit has changed */
if ((changed & GONE) != 0) {
// ...
requestLayout();
// ...
}
// ...
}
public void requestLayout() {
// ...
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
// mParent 为 ViewRootImpl
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
// ...
}
隐藏启动窗口根 View,会导致触发 ViewRootImpl#requestLayout(),它会触发一次 traversal,如下
// ViewRootImpl.java
private void performTraversals() {
// ...
// 此时可见性为 GONE
final int viewVisibility = getHostVisibility();
final String viewVisibilityReason = getHostVisibilityReason();
// true
final boolean viewVisibilityChanged = !mFirst
&& (mViewVisibility != viewVisibility || mNewSurfaceNeeded
// Also check for possible double visibility update, which will make current
// viewVisibility value equal to mViewVisibility and we may miss it.
|| mAppVisibilityChanged);
// ...
// mWinFrame 保存的是窗口 frame
Rect frame = mWinFrame;
if (mFirst) {
// ...
} else {
// 获取 desired width 和 desired height,用于首次测量
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
// ...
}
}
// ...
// mLayoutRequested 是在 requestLayout() 中设置为 true 的
// mStopped 和 mReportNextDraw,在没有特殊处理之前,都为 false
// 因此,layoutRequested 为 true,表示需要执行 layout
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
// ...
// 1. 执行首次测量
windowSizeMayChange |= measureHierarchy(host, lp, mView.getContext().getResources(),
desiredWindowWidth, desiredWindowHeight, shouldOptimizeMeasure);
}
// ...
if (layoutRequested) {
mLayoutRequested = false;
}
// ...
// viewVisibilityChanged 此时是 true
// 可见性改变,会触发 relayout window
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
|| mForceNextWindowRelayout) {
// ...
try {
// ...
// 2.执行 relayout window
relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
// ...
} catch (RemoteException e) {
} finally {
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
// ...
} else {
// ...
}
// ...
// true
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
// 3. 执行 layout
performLayout(lp, mWidth, mHeight);
// ...
}
// ...
if (!isViewVisible) { // 4. View 不可见,不需要绘制
if (mLastTraversalWasVisible) {
// systrace,log,event log 记录了不绘制的原因
logAndTrace("Not drawing due to not visible. Reason=" + viewVisibilityReason);
}
// ...
} else if (cancelAndRedraw) {
// ...
} else {
// ...
}
// ...
}
这次 traversal,主要关心 relayout window 即可,如下
// ViewRootImpl.java
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
// ...
final int requestedWidth = (int) (measuredWidth * appScale + 0.5f);
final int requestedHeight = (int) (measuredHeight * appScale + 0.5f);
int relayoutResult = 0;
mRelayoutSeq++;
if (relayoutAsync) {
// ...
} else {
if (windowSessionRelayoutInfo()) {
// 1.向 WMS 发起 relayout window
// 参数 viewVisibility 为 GONE
relayoutResult = mWindowSession.relayout(mWindow, params,
requestedWidth, requestedHeight, viewVisibility,
insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
mRelayoutSeq, mLastSyncSeqId, mRelayoutResult);
} else {
// ...
}
mRelayoutRequested = true;
// ...
}
// ...
// WMS 在 relayout window 会 release mSurfaceControl 底层关联的 surface
if (mSurfaceControl.isValid()) {
// ...
} else {
if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.pause()) {
mDirty.set(0, 0, mWidth, mHeight);
}
// 清理 mSurface,mSurfaceControl, mBlastBufferQueue 等等
destroySurface();
}
// ...
// mWinFrame 保存 relayout window 返回的窗口 frame
setFrame(mTmpFrames.frame, true /* withinRelayout */);
return relayoutResult;
}
此次 relayout window,其实就是通知 WMS 启动窗口 View 的可见性为 GONE,来看下 WMS 如何处理
// WindowManagerService.java
private int relayoutWindowInner(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags, int seq,
int lastSyncSeqId, ClientWindowFrames outFrames,
MergedConfiguration outMergedConfiguration, SurfaceControl outSurfaceControl,
InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
Bundle outBundle, WindowRelayoutResult outRelayoutResult) {
// ...
synchronized (mGlobalLock) {
// 启动窗口的 WindowState
final WindowState win = windowForClientLocked(session, client, false);
// ...
// WindowState#mViewVisibility 保存 View Visibility
// viewVisibility 此时为 GONE
win.setViewVisibility(viewVisibility);
// ...
// viewVisibility 为 GONE
// 因此,shouldRelayout 为 false
final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
(win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
|| win.mActivityRecord.isClientVisible());
if (!shouldRelayout && winAnimator.hasSurface() && !win.mAnimatingExit) {
// ...
// RELAYOUT_RES_SURFACE_CHANGED 此时代表即将 hide surface
result |= RELAYOUT_RES_SURFACE_CHANGED;
// ...
// 1. 执行 exiting animation
tryStartExitingAnimation(win, winAnimator);
}
if (shouldRelayout && outSurfaceControl != null) {
// ...
}
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
if (shouldRelayout) {
// ...
} else {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_2");
winAnimator.mEnterAnimationPending = false;
winAnimator.mEnteringAnimation = false;
if (outSurfaceControl != null) {
if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
// ...
} else {
if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmReleaseOutSurface_"
+ win.mAttrs.getTitle());
// 2. 释放 outSurfaceControl 关联的底层 surface
// 那么,ViewRootImpl#mSurfaceControl 就是一个无效的 surface
outSurfaceControl.release();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
// ...
}
Binder.restoreCallingIdentity(origId);
return result;
}
由于可见性为 GONE,WMS 对启动窗口 WindowState 执行了一个 exiting animation 动画
// WindowManagerService.java
private void tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator) {
int transit = WindowManagerPolicy.TRANSIT_EXIT;
// win 此时是启动窗口,它的 type 就是 TYPE_APPLICATION_STARTING
if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
}
if (win.isVisible() && win.isDisplayed() && win.mDisplayContent.okToAnimate()) {
String reason = null;
// 执行动画
if (winAnimator.applyAnimationLocked(transit, false)) {
// This is a WMCore-driven window animation.
reason = "applyAnimation";
} else if (win.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)) {
// ...
} else {
// ...
}
if (reason != null) {
// 标记启动窗口 WindowState 正在执行 exiting animation
win.mAnimatingExit = true;
ProtoLog.d(WM_DEBUG_ANIM,
"Set animatingExit: reason=startExitingAnimation/%s win=%s", reason, win);
}
}
// 如果不执行 exiting animation,就直接移除 surface
if (!win.mAnimatingExit) {
// ...
}
// ...
}
// WindowStateAnimator.java
// transit 为 TRANSIT_PREVIEW_DONE
// isEntrance 为 false
boolean applyAnimationLocked(int transit, boolean isEntrance) {
// ...
// Only apply an animation if the display isn't frozen. If it is
// frozen, there is no reason to animate and it can cause strange
// artifacts when we unfreeze the display if some different animation
// is running.
if (mWin.mToken.okToAnimate()) {
// 1. 获取动画 resource id
int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
int attr = -1;
Animation a = null;
if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
if (anim != DisplayPolicy.ANIMATION_NONE) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation");
// 2. 加载为 Animation
a = AnimationUtils.loadAnimation(mContext, anim);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
} else {
// ...
}
if (ProtoLog.isEnabled(WM_DEBUG_ANIM, LogLevel.VERBOSE)) {
ProtoLog.v(WM_DEBUG_ANIM, "applyAnimation: win=%s"
+ " anim=%d attr=0x%x a=%s transit=%d type=%d isEntrance=%b Callers %s",
this, anim, attr, a, transit, mAttrType, isEntrance, Debug.getCallers(20));
}
if (a != null) {
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
// 3. 由启动窗口 WindowState 执行动画
mWin.startAnimation(a);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
// false
mAnimationIsEntrance = isEntrance;
}
} else {
// ...
}
// true
return mWin.isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
}
为启动窗口的 exiting animation 加载的是一个 fade out 动画资源,如下
// DisplayPolicy.java
int selectAnimation(WindowState win, int transit) {
ProtoLog.i(WM_DEBUG_ANIM, "selectAnimation in %s: transit=%d", win, transit);
// transit 此时就是 TRANSIT_PREVIEW_DONE
if (transit == TRANSIT_PREVIEW_DONE) {
if (win.hasAppShownWindows()) {
if (win.isActivityTypeHome()) {
// ...
}
ProtoLog.i(WM_DEBUG_ANIM, "**** STARTING EXIT");
// 这个就是启动窗口 exiting animation 的动画资源
return R.anim.app_starting_exit;
}
}
// 对于一般的窗口的 WindowState 而言,返回这个值,代表需要从 theme 中获取窗口退出动画
return ANIMATION_STYLEABLE;
}
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:detachWallpaper="true"
android:interpolator="@interpolator/linear"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="150" />
知道做什么动画,接下来看下 WindowState 如何执行这个动画
// WindowState.java
void startAnimation(Animation anim) {
// If we are an inset provider, all our animations are driven by the inset client.
if (mControllableInsetProvider != null) {
return;
}
final DisplayInfo displayInfo = getDisplayInfo();
anim.initialize(mWindowFrames.mFrame.width(), mWindowFrames.mFrame.height(),
displayInfo.appWidth, displayInfo.appHeight);
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked());
final Point position = new Point();
if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement()) {
transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
position);
} else {
position.set(mSurfacePosition);
}
final AnimationAdapter adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(anim, position, false /* canSkipFirstFrame */,
0 /* windowCornerRadius */),
mWmService.mSurfaceAnimationRunner);
// 执行动画
// 注意,这里使用的是 pending transaction
startAnimation(getPendingTransaction(), adapter);
// 在动画线程中,提交 pending transaction
commitPendingTransaction();
}
private void startAnimation(Transaction t, AnimationAdapter adapter) {
// 调用基类 WindowContainer 的方法执行动画
// 动画类型为 ANIMATION_TYPE_WINDOW_ANIMATION
startAnimation(t, adapter, mWinAnimator.mLastHidden, ANIMATION_TYPE_WINDOW_ANIMATION);
}
这里就是一个 WindowContainer 的 local animation,请读者自行根据 Android V : WindowContainer 本地动画框架 自行分析。
这里直接看下动画结束的流程
// WindowState.java
protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
super.onAnimationFinished(type, anim);
// 直接看这里
mWinAnimator.onAnimationFinished();
}
// WindowStateAnimator.java
void onAnimationFinished() {
// Done animating, clean up.
ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s: exiting=%b, reportedVisible=%b",
this, mWin.mAnimatingExit,
(mWin.mActivityRecord != null && mWin.mActivityRecord.reportedVisible));
// ...
// 1.
mWin.onExitAnimationDone();
// ...
}
// WindowState.java
void onExitAnimationDone() {
// ...
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Exit animation finished in %s: remove=%b",
this, mRemoveOnExit);
// 因为 exit animation 执行完成会 hide 窗口 surface,然后要根据情况销毁 WindowState
mDestroying = true;
// true
final boolean hasSurface = mWinAnimator.hasSurface();
// 1. hide 窗口 surface
// Use pendingTransaction here so hide is done the same transaction as the other
// animations when exiting
mWinAnimator.hide(getPendingTransaction(), "onExitAnimationDone");
// If we have an app token, we ask it to destroy the surface for us, so that it can take
// care to ensure the activity has actually stopped and the surface is not still in use.
// Otherwise we add the service to mDestroySurface and allow it to be processed in our next
// transaction.
if (mActivityRecord != null) {
if (mAttrs.type == TYPE_BASE_APPLICATION) {
} else {
// 2. 既然窗口 surface 已经被 hide,那么尝试 destroy surface
// 注意第二个参数,如果 app 端通知服务端 activity stop 完成,
// 服务端会设置 mActivityRecord.mAppStopped 为 true
destroySurface(false /* cleanupOnResume */, mActivityRecord.mAppStopped);
}
} else {
// ...
}
// WindowState 的 exit animation 结束
mAnimatingExit = false;
ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=exitAnimationDone win=%s", this);
getDisplayContent().mWallpaperController.hideWallpapers(this);
}
exit animation 结束时,首先会 hide 窗口 surface 。窗口 surface 是被 WindowSurfaceController 管理的,自然由它去 hide,如下
// WindowStateAnimator.java
void hide(SurfaceControl.Transaction transaction, String reason) {
if (!mLastHidden) {
// 窗口被隐藏
mLastHidden = true;
if (mSurfaceController != null) {
mSurfaceController.hide(transaction, reason);
}
}
}
// WindowSurfaceController.java
void hide(SurfaceControl.Transaction transaction, String reason) {
ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE HIDE ( %s ): %s", reason, title);
if (mSurfaceShown) {
hideSurface(transaction);
}
}
private void hideSurface(SurfaceControl.Transaction transaction) {
if (mSurfaceControl == null) {
return;
}
// mSurfaceShown 更新为 false
setShown(false);
try {
// hide surface
transaction.hide(mSurfaceControl);
// ...
} catch (RuntimeException e) {
Slog.w(TAG, "Exception hiding surface in " + this);
}
}
exit animation 执行完成,hide 了窗口 surface,
// WindowState.java
// cleanupOnResume 为 false
// appStopped 为 false,因为此时 activity 没有 stop
boolean destroySurface(boolean cleanupOnResume, boolean appStopped) {
boolean destroyedSomething = false;
final ArrayList<WindowState> childWindows = new ArrayList<>(mChildren);
for (int i = childWindows.size() - 1; i >= 0; --i) {
}
// 参数 appStopped 和 cleanupOnResume ,此时都为 false
// app 端移除 View 后,会通知 WMS 移除窗口,mWindowRemovalAllowed 才会为 true,此时为 false
if (!(appStopped || mWindowRemovalAllowed || cleanupOnResume)) {
return destroyedSomething;
}
// 不会执行下面移除 surface 的操作
if (mDestroying) {
ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s"
+ " destroySurfaces: appStopped=%b"
+ " win.mWindowRemovalAllowed=%b"
+ " win.mRemoveOnExit=%b", this, appStopped,
mWindowRemovalAllowed, mRemoveOnExit);
if (!cleanupOnResume || mRemoveOnExit) {
destroySurfaceUnchecked();
}
if (mRemoveOnExit) {
removeImmediately();
}
if (cleanupOnResume) {
requestUpdateWallpaperIfNeeded();
}
mDestroying = false;
destroyedSomething = true;
// Since mDestroying will affect ActivityRecord#allDrawn, we need to perform another
// traversal in case we are waiting on this window to start the transition.
if (getDisplayContent().mAppTransition.isTransitionSet()
&& getDisplayContent().mOpeningApps.contains(mActivityRecord)) {
mWmService.mWindowPlacerLocked.requestTraversal();
}
}
return destroyedSomething;
}
很可惜,由于此时 activity 没有 stop,并且 WMS 还没有收到 app 端移除窗口的通知。因此,exit animation 结束时,并不能立即移除窗口 surface。
移除启动窗口 root view
// WindowManagerGlobal.java
// immediate 为 false
public void removeView(View view, boolean immediate) {
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
throw new IllegalStateException("Calling with view " + view
+ " but the ViewAncestor is attached to " + curView);
}
}
private void removeViewLocked(int index, boolean immediate) {
ViewRootImpl root = mRoots.get(index);
View view = root.getView();
if (root != null) {
root.getImeFocusController().onWindowDismissed();
}
// 参数 immediate 为 false,代表不是立即移除,所以返回 true
boolean deferred = root.die(immediate);
if (view != null) {
view.assignParent(null);
if (deferred) {
mDyingViews.add(view);
}
}
}
// ViewRootImpl.java
boolean die(boolean immediate) {
if (immediate && !mIsInTraversal) {
}
if (!mIsDrawing) {
// 不是处于 draw 阶段,销毁硬件加速渲染
destroyHardwareRenderer();
} else {
Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
" window=" + this + ", title=" + mWindowAttributes.getTitle());
}
// 发送消息去移除 view
// 最终执行 doDie()
mHandler.sendEmptyMessage(MSG_DIE);
return true;
}
void doDie() {
checkThread();
if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
synchronized (this) {
if (mRemoved) {
return;
}
mRemoved = true;
// ...
if (mAdded) {
// 1. 从 ViewRootImpl 中移除 View 和 窗口
dispatchDetachedFromWindow();
}
if (mAdded && !mFirst) {
// 销毁硬件加速渲染
destroyHardwareRenderer();
if (mView != null) {
int viewVisibility = mView.getVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
if (mWindowAttributesChanged || viewVisibilityChanged) {
}
// 清理 mSurface、mSurfaceControl、mBlastBufferQueue 等等
destroySurface();
}
}
// ...
mAdded = false;
AnimationHandler.removeRequestor(this);
}
handleSyncRequestWhenNoAsyncDraw(mActiveSurfaceSyncGroup, mHasPendingTransactions,
mPendingTransaction, "shutting down VRI");
// 从 WindowManagerGlobal 的数组 mRoots,mParams,mViews 进行移除
WindowManagerGlobal.getInstance().doRemoveView(this);
}
// ViewRootImpl.java
void dispatchDetachedFromWindow() {
// ...
// 移除 View
mView.assignParent(null);
mView = null;
// ...
try {
// 通知 WMS 移除窗口
mWindowSession.remove(mWindow.asBinder());
} catch (RemoteException e) {
}
// ...
}
WindowManagerGlobal 移除 View,目前主要看下 WMS 如何移除窗口,其他的一些操作请自行分析
// WindowManagerService.java
void removeClientToken(Session session, IBinder client) {
synchronized (mGlobalLock) {
// 启动窗口 WindowState
WindowState win = windowForClientLocked(session, client, false);
if (win != null) {
// 1.
win.removeIfPossible();
return;
}
// ...
}
}
// WindowState.java
void removeIfPossible() {
// 看来它表示,只有 app 端要求移除窗口,服务端才能移除窗口
mWindowRemovalAllowed = true;
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
"removeIfPossible: %s callers=%s", this, Debug.getCallers(5));
// true
// 启动窗口有 starting data
final boolean startingWindow = mStartingData != null;
if (startingWindow) {
ProtoLog.d(WM_DEBUG_STARTING_WINDOW, "Starting window removed %s", this);
// 1.
// 移除启动窗口时,在 WMShell 对真窗 WindowState 执行了一个 local animation,旨在获取真窗 leash
// 此时,app 端要求移除窗口,那么是时候取消它了
// Cancel the remove starting window animation on shell. The main window might changed
// during animating, checking for all windows would be safer.
if (mActivityRecord != null) {
mActivityRecord.forAllWindows(w -> {
if (w.isSelfAnimating(0, ANIMATION_TYPE_STARTING_REVEAL)) {
w.cancelAnimation();
return true;
}
return false;
}, true);
}
mTransitionController.mTransitionTracer.logRemovingStartingWindow(mStartingData);
} else if (mAttrs.type == TYPE_BASE_APPLICATION
&& isSelfAnimating(0, ANIMATION_TYPE_STARTING_REVEAL)) {
// ...
}
ProtoLog.v(WM_DEBUG_FOCUS, "Remove client=%x, surfaceController=%s Callers=%s",
System.identityHashCode(mClient.asBinder()),
mWinAnimator.mSurfaceController,
Debug.getCallers(5));
final DisplayContent displayContent = getDisplayContent();
final long origId = Binder.clearCallingIdentity();
try {
disposeInputChannel();
mOnBackInvokedCallbackInfo = null;
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Remove %s: mSurfaceController=%s mAnimatingExit=%b mRemoveOnExit=%b "
+ "mHasSurface=%b surfaceShowing=%b animating=%b app-animation=%b "
+ "mDisplayFrozen=%b callers=%s",
this, mWinAnimator.mSurfaceController, mAnimatingExit, mRemoveOnExit,
mHasSurface, mWinAnimator.getShown(),
isAnimating(TRANSITION | PARENTS),
mActivityRecord != null && mActivityRecord.isAnimating(PARENTS | TRANSITION),
mWmService.mDisplayFrozen, Debug.getCallers(6));
// Visibility of the removed window. Will be used later to update orientation later on.
boolean wasVisible = false;
// 2. 窗口有 surface
// First, see if we need to run an animation. If we do, we have to hold off on removing the
// window until the animation is done. If the display is frozen, just remove immediately,
// since the animation wouldn't be seen.
if (mHasSurface && mToken.okToAnimate()) {
// 窗口没有 surface、或者正在执行 exit animation、或者 exit animation 执行完成
// 那么,窗口均不可见
// If we are not currently running the exit animation, we need to see about starting one
wasVisible = isVisible();
// allowExitAnimation 为 true,因此此时没有屏幕旋转动画,窗口所在的 Activity 也没有重启
// Remove immediately if there is display transition because the animation is
// usually unnoticeable (e.g. covered by rotation animation) and the animation
// bounds could be inconsistent, such as depending on when the window applies
// its draw transaction with new rotation.
final boolean allowExitAnimation = !displayContent.inTransition()
// There will be a new window so the exit animation may not be visible or
// look weird if its orientation is changed.
&& !inRelaunchingActivity();
// 2.1 wasVisible 为 true,在此时代表窗口没有执行过 exiting animation
// 既然没有执行 exit animation,那执行一个
// 很显然目前正在执行 exitng animation,或者执行结束了
if (wasVisible && isDisplayed()) {
// ..
}
final boolean isAnimating = allowExitAnimation
&& (mAnimatingExit || isAnimationRunningSelfOrParent());
// 这里检测 ActivityRecord 下是否只有启动窗口
// 很显然,此时还有一个真窗
// 所以,lastWindowIsStartingWindow 为 false
final boolean lastWindowIsStartingWindow = startingWindow && mActivityRecord != null
&& mActivityRecord.isLastWindow(this);
// 2.2 如果窗口正在执行 exit animation,那么标记 exit animation 完成后移除窗口及其 surface
// We delay the removal of a window if it has a showing surface that can be used to run
// exit animation and it is marked as exiting.
// Also, If isn't the an animating starting window that is the last window in the app.
// We allow the removal of the non-animating starting window now as there is no
// additional window or animation that will trigger its removal.
if (mWinAnimator.getShown() && !lastWindowIsStartingWindow && isAnimating) {
// Make isSelfOrAncestorWindowAnimatingExit return true so onExitAnimationDone
// can proceed to remove this window.
mAnimatingExit = true;
// The exit animation is running or should run... wait for it!
ProtoLog.v(WM_DEBUG_ADD_REMOVE,
"Not removing %s due to exit animation", this);
ProtoLog.v(WM_DEBUG_ANIM, "Set animatingExit: reason=remove/isAnimating win=%s",
this);
// mRemoveOnExit 设置为 true,代表 exit animation 结束后可以移除 surface
setupWindowForRemoveOnExit();
if (mActivityRecord != null) {
mActivityRecord.updateReportedVisibilityLocked();
}
return;
}
}
// ...
// 3. 如果窗口连 surface 都没有,立即移除窗口及其 surface
removeImmediately();
// ...
} finally {
Binder.restoreCallingIdentity(origId);
}
}
前面提到过,在移除启动窗口时,对真窗做了一个动画,只为获得 leash,然后这个动画无法结束。此时,app 端要求移除窗口,恰好就是取消它的时机,其实就是强制结束动画。根据 Android V : WindowContainer 本地动画框架 分析,当动画结束后,会 remove leash,并把 WindowContainer surface 归位,请读者自行分析。
此时,启动窗口 WindowState 确实在执行 exit animation,但不一定结束了。根据 Android V : WindowContainer 本地动画框架 可知,动画时用 ValueAnimator 执行的,需要一点点时间的,虽然不多。并且我自己抓 log 也看过,此时 exit animation 确实还没有执行完,那么会走到 2.2 步,标记 exit aninamtion 完成时,再移除窗口及其 surface。
OK,让我们再回到 exit animation 完成时,移除窗口及其 surface 的方法
// WindowState.java
void onExitAnimationDone() {
// ...
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Exit animation finished in %s: remove=%b",
this, mRemoveOnExit);
mDestroying = true;
final boolean hasSurface = mWinAnimator.hasSurface();
// 1.hide 窗口 surface
mWinAnimator.hide(getPendingTransaction(), "onExitAnimationDone");
if (mActivityRecord != null) {
if (mAttrs.type == TYPE_BASE_APPLICATION) {
// ...
} else {
// 2.destroy surface
destroySurface(false /* cleanupOnResume */, mActivityRecord.mAppStopped);
}
} else {
// ...
}
mAnimatingExit = false;
ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=exitAnimationDone win=%s", this);
getDisplayContent().mWallpaperController.hideWallpapers(this);
}
boolean destroySurface(boolean cleanupOnResume, boolean appStopped) {
boolean destroyedSomething = false;
// Copying to a different list as multiple children can be removed.
final ArrayList<WindowState> childWindows = new ArrayList<>(mChildren);
for (int i = childWindows.size() - 1; i >= 0; --i) {
final WindowState c = childWindows.get(i);
destroyedSomething |= c.destroySurface(cleanupOnResume, appStopped);
}
// 此时 mWindowRemovalAllowed 为 true
if (!(appStopped || mWindowRemovalAllowed || cleanupOnResume)) {
return destroyedSomething;
}
// exit animation 执行完成是,就设置 mDestroying 为 true
if (mDestroying) {
ProtoLog.e(WM_DEBUG_ADD_REMOVE, "win=%s"
+ " destroySurfaces: appStopped=%b"
+ " win.mWindowRemovalAllowed=%b"
+ " win.mRemoveOnExit=%b", this, appStopped,
mWindowRemovalAllowed, mRemoveOnExit);
// cleanupOnResume 此时为 true
// mRemoveOnExit 在移除窗口时,用来标记 exit animation 完成时移除窗口及其 surface
if (!cleanupOnResume || mRemoveOnExit) {
// 1. 移除窗口 surface
destroySurfaceUnchecked();
}
// 2. 移除窗口
if (mRemoveOnExit) {
removeImmediately();
}
// cleanupOnResume 此时为 false
if (cleanupOnResume) {
// 看来 cleanupOnResume 与壁纸更新相关
requestUpdateWallpaperIfNeeded();
}
// 代表窗口及其 surface 已经销毁
mDestroying = false;
destroyedSomething = true;
// ...
}
return destroyedSomething;
}
移除窗口 surface ,当然由 WindowSurafceController 完成,如下
// WindowState.java
void destroySurfaceUnchecked() {
// 1.mTmpTransaction 保存移除窗口surface 操作
mWinAnimator.destroySurfaceLocked(mTmpTransaction);
// 立即 apply mTmpTransaction,即立即移除窗口 surface
mTmpTransaction.apply();
// Clear animating flags now, since the surface is now gone. (Note this is true even
// if the surface is saved, to outside world the surface is still NO_SURFACE.)
mAnimatingExit = false;
ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this);
// ...
}
// WindowStateAnimator.java
void destroySurfaceLocked(SurfaceControl.Transaction t) {
if (mSurfaceController == null) {
return;
}
// 窗口被隐藏
mWin.mHidden = true;
try {
if (DEBUG_VISIBILITY) {
logWithStack(TAG, "Window " + this + " destroying surface "
+ mSurfaceController + ", session " + mSession);
}
ProtoLog.i(WM_SHOW_SURFACE_ALLOC, "SURFACE DESTROY: %s. %s",
mWin, new RuntimeException().fillInStackTrace());
// 1.
destroySurface(t);
// ...
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying Window " + this
+ " surface " + mSurfaceController + " session " + mSession + ": "
+ e.toString());
}
// WindowState#mHasSurface 更新为 false,代表没有 surface
mWin.setHasSurface(false);
if (mSurfaceController != null) {
// WindowSurfaceController#mSurfaceShown 更新为 false,代表窗口不可见
// 其实前面 hide 窗口surface 时,已经设置过
mSurfaceController.setShown(false);
}
// 清理 WindowSurfaceController
mSurfaceController = null;
// 绘制状态重置为 NO_SURFACE
mDrawState = NO_SURFACE;
}
void destroySurface(SurfaceControl.Transaction t) {
try {
if (mSurfaceController != null) {
// 由 WindowSurfaceController destory 窗口 surface
mSurfaceController.destroy(t);
}
} catch (RuntimeException e) {
Slog.w(TAG, "Exception thrown when destroying surface " + this
+ " surface " + mSurfaceController + " session " + mSession + ": " + e);
} finally {
mWin.setHasSurface(false);
mSurfaceController = null;
mDrawState = NO_SURFACE;
}
}
窗口 surface 被 destory 后,接下来轮到移除 WindowState,如下
// WindowState.java
void removeImmediately() {
// ...
mRemoved = true;
// 前面已经移除了窗口 surface
mWinAnimator.destroySurfaceLocked(getSyncTransaction());
if (!mDrawHandlers.isEmpty()) {
mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
}
// 通过基类 WindowContainer 方法移除自己(WindowState)
super.removeImmediately();
// ...
// WindowState 被移除会有很多影响,WMS 做这些善后的事情
// 例如,WindowToken 是否处理、处理壁纸、layer 更新
mWmService.postWindowRemoveCleanupLocked(this);
consumeInsetsChange();
}
// WindowContainer.java
void removeImmediately() {
// ...
// 1. 先移除 WindowState surface
if (mSurfaceControl != null) {
getSyncTransaction().remove(mSurfaceControl);
setSurfaceControl(null);
mLastSurfacePosition.set(0, 0);
mLastDeltaRotation = Surface.ROTATION_0;
scheduleAnimation();
}
// ...
// 2. 再通过 parent 移除自己 (WindowState)
// 也就是从窗口层级树中移除
// This must happen after updating the surface so that sync transactions can be handled
// properly.
if (mParent != null) {
mParent.removeChild(this);
}
for (int i = mListeners.size() - 1; i >= 0; --i) {
mListeners.get(i).onRemoved();
}
}
至此,本文分析结束。其实,在 exit animation 执行完成是 hide 窗口 surface,真窗就已经显示出来了。窗口的移除,还得 app 端发起请求才得以被执行。