Android V app 冷启动(12) 真窗显示

513 阅读13分钟

上一篇文章分析了启动窗口的移除,它主要是做了一个 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 端发起请求才得以被执行。