目前,屏幕上显示的仍然是启动窗口,而真窗位于其下。那么,当真窗绘制完成后,是如何显示出来呢,从本文开始,将逐步揭晓。
真窗绘制完成
当真窗绘制完成,会上报 WMS,如下
// WindowManagerService.java
void finishDrawingWindow(Session session, IWindow client,
@Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
if (postDrawTransaction != null) {
postDrawTransaction.sanitize(Binder.getCallingPid(), Binder.getCallingUid());
}
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
// 获取对应的 WindowState
WindowState win = windowForClientLocked(session, client, false);
ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s",
win, (win != null ? win.mWinAnimator.drawStateToString() : "null"));
// 1. 由 WindowState 处理 finish drawing
if (win != null && win.finishDrawing(postDrawTransaction, seqId)) {
if (win.hasWallpaper()) {
win.getDisplayContent().pendingLayoutChanges |=
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
}
// 标记 DisplayContent 需要 layout
win.setDisplayLayoutNeeded();
// 2. 请求窗口刷新
mWindowPlacerLocked.requestTraversal();
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
在 Android V app 冷启动(5) 窗口动画就绪 中分析过 WMS 处理启动窗口 finish drawing,并且在执行 Transition 动画时, ActivityRecord#mVisible 被更新为 true。那么,WMS 处理真窗的 finish drawing,会导致真窗的绘制状态切换到 HAS_DRAWN,并且在 prepare surface 中,会 show 真窗 surface。
当真窗绘制完成,在窗口刷新的时候,还会触发启动窗口的移除,如下
// WindowState.java
boolean performShowLocked() {
// ...
final int drawState = mWinAnimator.mDrawState;
if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
if (mAttrs.type != TYPE_APPLICATION_STARTING) {
// ActivityRecord 处理真窗显示
mActivityRecord.onFirstWindowDrawn(this);
} else {
// ...
}
}
// ActivityRecord#mVisible 此时为 true,可以 show 真窗 surface
if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
return false;
}
// ...
// Force the show in the next prepareSurfaceLocked() call.
mWinAnimator.mLastAlpha = -1;
ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
// 绘制状态切换到 HAS_DRAWN
mWinAnimator.mDrawState = HAS_DRAWN;
// ..
return true;
}
// ActivityRecord.java
void onFirstWindowDrawn(WindowState win) {
firstWindowDrawn = true;
// ...
// Remove starting window directly if is in a pure task. Otherwise if it is associated with
// a task (e.g. nested task fragment), then remove only if all visible windows in the task
// are drawn.
final Task associatedTask = task.mSharedStartingData != null ? task : null;
if (associatedTask == null) {
// 真窗绘制完成,就开始移除启动窗口
removeStartingWindow();
} else if (associatedTask.getActivity(
r -> r.isVisibleRequested() && !r.firstWindowDrawn) == null) {
// ...
}
updateReportedVisibilityLocked();
}
void removeStartingWindow() {
// ...
removeStartingWindowAnimation(true /* prepareAnimation */);
// ...
}
// prepareAnimation 为 true
void removeStartingWindowAnimation(boolean prepareAnimation) {
// ...
final StartingSurfaceController.StartingSurface surface;
final boolean animate;
final boolean hasImeSurface;
if (mStartingData != null) {
// 如果正在执行 Transition 动画,或者 Transition 正在收集当前 ActivityRecord
// 那么,此时不应该移除启动窗口,而是在 ActivityRecord#onSyncTransactionCommitted()
// 中继续移除启动窗口
if (mStartingData.mWaitForSyncTransactionCommit
|| mTransitionController.isCollecting(this)) {
mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
mStartingData.mPrepareRemoveAnimation = prepareAnimation;
return;
}
// true
animate = prepareAnimation && mStartingData.needRevealAnimation()
&& mStartingWindow.isVisibleByPolicy();
// false
hasImeSurface = mStartingData.hasImeSurface();
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
+ " animate=%b Callers=%s", this, mStartingWindow, animate,
Debug.getCallers(5));
surface = mStartingSurface;
mStartingData = null;
mStartingSurface = null;
mStartingWindow = null;
mTransitionChangeFlags &= ~FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
if (surface == null) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "startingWindow was set but "
+ "startingSurface==null, couldn't remove");
return;
}
} else {
// ...
}
// StartingSurfaceController 移除启动窗口
surface.remove(animate, hasImeSurface);
}
// StartingSurfaceController.java
public class StartingSurfaceController {
// ...
final class StartingSurface {
private final Task mTask;
final ITaskOrganizer mTaskOrganizer;
StartingSurface(Task task, ITaskOrganizer taskOrganizer) {
mTask = task;
mTaskOrganizer = taskOrganizer;
}
public void remove(boolean animate, boolean hasImeSurface) {
synchronized (mService.mGlobalLock) {
// 通知 WMShell 移除启动窗口
mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask,
mTaskOrganizer, animate, hasImeSurface);
}
}
}
}
// TaskOrganizerController.java
void removeStartingWindow(Task task, ITaskOrganizer taskOrganizer, boolean prepareAnimation,
boolean hasImeSurface) {
final Task rootTask = task.getRootTask();
if (rootTask == null) {
return;
}
// WMShell 会注册一个 ITaskOrganizer
final ITaskOrganizer lastOrganizer = taskOrganizer != null ? taskOrganizer
: getTaskOrganizer();
if (lastOrganizer == null) {
return;
}
// StartingWindowRemovalInfo 包装了移除启动窗口的数据
final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo();
removalInfo.taskId = task.mTaskId;
// true
// 代表移除启动窗口时,需要以动画的方式执行
removalInfo.playRevealAnimation = prepareAnimation
&& task.getDisplayContent() != null
&& task.getDisplayInfo().state == Display.STATE_ON;
// true
// 表示需要对真窗 leash 做动画
final boolean playShiftUpAnimation = !task.inMultiWindowMode();
final ActivityRecord topActivity = task.topActivityContainsStartingWindow();
if (topActivity != null) {
// Set defer remove mode for IME
final DisplayContent dc = topActivity.getDisplayContent();
if (hasImeSurface) {
}
// 这里获取到的是真窗 WindowState
final WindowState mainWindow =
topActivity.findMainWindow(false/* includeStartingApp */);
if (mainWindow == null || mainWindow.mRemoved) {
// ...
} else if (removalInfo.playRevealAnimation && playShiftUpAnimation) {
removalInfo.roundedCornerRadius =
topActivity.mLetterboxUiController.getRoundedCornersRadius(mainWindow);
// 此时并不是真正对真窗 WindowState 做动画,而只是为了获得真窗 leash
removalInfo.windowAnimationLeash = applyStartingWindowAnimation(mainWindow);
// 真窗 frame
removalInfo.mainFrame = new Rect(mainWindow.getFrame());
removalInfo.mainFrame.offsetTo(mainWindow.mSurfacePosition.x,
mainWindow.mSurfacePosition.y);
}
}
try {
// 通知 WMShell 执行移除启动窗口
lastOrganizer.removeStartingWindow(removalInfo);
} catch (RemoteException e) {
Slog.e(TAG, "Exception sending onStartTaskFinished callback", e);
}
}
WMShell 移除启动窗口
// ShellTaskOrganizer.java
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
if (mStartingWindow != null) {
mStartingWindow.removeStartingWindow(removalInfo);
}
}
// StartingWindowController.java
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.removeStartingWindow(
removalInfo));
// ...
}
// StartingSurfaceDrawer.java
public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) {
if (removalInfo.windowlessSurface) {
// ...
} else {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"Task start finish, remove starting surface for task: %d",
removalInfo.taskId);
mWindowRecords.removeWindow(removalInfo, removalInfo.removeImmediately);
}
}
// StartingSurfaceDrawer.java
public class StartingSurfaceDrawer {
static class StartingWindowRecordManager {
void removeWindow(StartingWindowRemovalInfo removeInfo, boolean immediately) {
final int taskId = removeInfo.taskId;
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
if (record != null) {
final boolean canRemoveRecord = record.removeIfPossible(removeInfo, immediately);
if (canRemoveRecord) {
mStartingWindowRecords.remove(taskId);
}
}
}
}
}
// SplashscreenWindowCreator.java
class SplashscreenWindowCreator extends AbsSplashWindowCreator {
private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord {
public boolean removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) {
// ...
// immediately 为 false, mSuggestType 为 STARTING_WINDOW_TYPE_SPLASH_SCREEN
if (immediately
|| mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) {
// ...
} else {
if (info.playRevealAnimation) {
mSplashscreenContentDrawer.applyExitAnimation(mSplashView,
info.windowAnimationLeash, info.mainFrame,
// 注意,这个参数是动画执行完成的回调
() -> removeWindowInner(mRootView, true),
mCreateTime, info.roundedCornerRadius);
} else {
// ...
}
}
return true;
}
}
}
// SplashscreenContentDrawer.java
/**
* Create and play the default exit animation for splash screen view.
*/
void applyExitAnimation(SplashScreenView view, SurfaceControl leash,
Rect frame, Runnable finishCallback, long createTime, float roundedCornerRadius) {
final Runnable playAnimation = () -> {
final SplashScreenExitAnimation animation = new SplashScreenExitAnimation(mContext,
view, leash, frame, mMainWindowShiftLength, mTransactionPool, finishCallback,
roundedCornerRadius);
animation.startAnimations();
};
// ...
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"applyExitAnimation delayed: %s", delayed);
// 无论走那条路,最终都会执行 playAnimation
if (delayed > 0) {
view.postDelayed(playAnimation, delayed);
} else {
playAnimation.run();
}
}
// SplashScreenExitAnimation.java
SplashScreenExitAnimation(Context context, SplashScreenView view, SurfaceControl leash,
Rect frame, int mainWindowShiftLength, TransactionPool pool, Runnable handleFinish,
float roundedCornerRadius) {
mSplashScreenView = view;
mFirstWindowSurface = leash;
mRoundedCornerRadius = roundedCornerRadius;
if (frame != null) {
mFirstWindowFrame.set(frame);
}
View iconView = view.getIconView();
// If the icon and the background are invisible, don't animate it
if (iconView == null || iconView.getLayoutParams().width == 0
|| iconView.getLayoutParams().height == 0) {
} else {
iconView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
// The branding view could only exists when the icon is present.
final View brandingView = view.getBrandingView();
if (brandingView != null) {
mBrandingStartAlpha = brandingView.getAlpha();
} else {
mBrandingStartAlpha = 0;
}
mIconFadeOutDuration = context.getResources().getInteger(
R.integer.starting_window_app_reveal_icon_fade_out_duration);
mAppRevealDelay = context.getResources().getInteger(
R.integer.starting_window_app_reveal_anim_delay);
mIconStartAlpha = iconView.getAlpha();
}
mAppRevealDuration = context.getResources().getInteger(
R.integer.starting_window_app_reveal_anim_duration);
// 对于手机/平板来说,mAnimationType 为 0,动画的方式是 radial vanish + slide up
// 可以把这个配置改为 1,动画的方式是 fade out
// TODO:对于性能比较差的手机/平板,把配置改为1,是否对性能有帮助?
mAnimationType = context.getResources().getInteger(
R.integer.starting_window_exit_animation_type);
mAnimationDuration = Math.max(mIconFadeOutDuration, mAppRevealDelay + mAppRevealDuration);
mMainWindowShiftLength = mainWindowShiftLength;
mFinishCallback = handleFinish;
mTransactionPool = pool;
}
void startAnimations() {
SplashScreenExitAnimationUtils.startAnimations(mAnimationType, mSplashScreenView,
mFirstWindowSurface, mMainWindowShiftLength, mTransactionPool, mFirstWindowFrame,
mAnimationDuration, mIconFadeOutDuration, mIconStartAlpha, mBrandingStartAlpha,
mAppRevealDelay, mAppRevealDuration, this, mRoundedCornerRadius);
}
// SplashScreenExitAnimationUtils.java
static void startAnimations(@ExitAnimationType int animationType,
ViewGroup splashScreenView, SurfaceControl firstWindowSurface,
int mainWindowShiftLength, TransactionPool transactionPool, Rect firstWindowFrame,
int animationDuration, int iconFadeOutDuration, float iconStartAlpha,
float brandingStartAlpha, int appRevealDelay, int appRevealDuration,
Animator.AnimatorListener animatorListener, float roundedCornerRadius) {
ValueAnimator animator;
if (animationType == TYPE_FADE_OUT) {
// ...
} else {
// 创建一个 ValueAnimator
animator = createRadialVanishSlideUpAnimator(splashScreenView,
firstWindowSurface, mainWindowShiftLength, transactionPool, firstWindowFrame,
animationDuration, iconFadeOutDuration, iconStartAlpha, brandingStartAlpha,
appRevealDelay, appRevealDuration, animatorListener, roundedCornerRadius);
}
// 执行 ValueAnimator
animator.start();
}
/**
* Creates the animator to fade out the icon, reveal the app, and shift up main window.
*/
private static ValueAnimator createRadialVanishSlideUpAnimator(ViewGroup splashScreenView,
SurfaceControl firstWindowSurface, int mMainWindowShiftLength,
TransactionPool transactionPool, Rect firstWindowFrame, int animationDuration,
int iconFadeOutDuration, float iconStartAlpha, float brandingStartAlpha,
int appRevealDelay, int appRevealDuration, Animator.AnimatorListener animatorListener,
float roundedCornerRadius) {
// reveal app
final float transparentRatio = 0.8f;
final int globalHeight = splashScreenView.getHeight();
final int verticalCircleCenter = 0;
final int finalVerticalLength = globalHeight - verticalCircleCenter;
final int halfWidth = splashScreenView.getWidth() / 2;
final int endRadius = (int) (0.5 + (1f / transparentRatio * (int)
Math.sqrt(finalVerticalLength * finalVerticalLength + halfWidth * halfWidth)));
final int[] colors = {Color.WHITE, Color.WHITE, Color.TRANSPARENT};
final float[] stops = {0f, transparentRatio, 1f};
// RadialVanishAnimation 是一个 View
// 在构造函数中,SplashScreenView 添加了 RadialVanishAnimation
// SplashScreenView 是一个 FrameLayout
RadialVanishAnimation radialVanishAnimation = new RadialVanishAnimation(splashScreenView);
radialVanishAnimation.setCircleCenter(halfWidth, verticalCircleCenter);
radialVanishAnimation.setRadius(0 /* initRadius */, endRadius);
radialVanishAnimation.setRadialPaintParam(colors, stops);
View occludeHoleView = null;
ShiftUpAnimation shiftUpAnimation = null;
if (firstWindowSurface != null && firstWindowSurface.isValid()) {
if (DEBUG_EXIT_ANIMATION_BLEND) {
// ...
} else if (splashScreenView instanceof SplashScreenView) {
occludeHoleView.setBackgroundColor(
((SplashScreenView) splashScreenView).getInitBackgroundColor());
} else {
// ...
}
// mMainWindowShiftLength 是 20 dp
final ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, mMainWindowShiftLength);
// SplashScreenView 保存 occludeHoleView
// SplashScreenView 是 FrameLayout
splashScreenView.addView(occludeHoleView, params);
// ShiftUpAnimation 一是操作 occludeHoleView,而是操作真窗 leash
shiftUpAnimation = new ShiftUpAnimation(0, -mMainWindowShiftLength, occludeHoleView,
firstWindowSurface, splashScreenView, transactionPool, firstWindowFrame,
mMainWindowShiftLength, roundedCornerRadius);
}
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
animator.setDuration(animationDuration);
animator.setInterpolator(Interpolators.LINEAR);
// animatorListener 由 SplashScreenExitAnimation 实现
if (animatorListener != null) {
animator.addListener(animatorListener);
}
View finalOccludeHoleView = occludeHoleView;
ShiftUpAnimation finalShiftUpAnimation = shiftUpAnimation;
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (finalShiftUpAnimation != null) {
finalShiftUpAnimation.finish();
}
// 动画结束,移除这两个动画的 View
splashScreenView.removeView(radialVanishAnimation);
splashScreenView.removeView(finalOccludeHoleView);
}
});
animator.addUpdateListener(animation -> {
float linearProgress = (float) animation.getAnimatedValue();
// Fade out progress
final float iconProgress =
ICON_INTERPOLATOR.getInterpolation(getProgress(
linearProgress, 0 /* delay */, iconFadeOutDuration, animationDuration));
View iconView = null;
View brandingView = null;
if (splashScreenView instanceof SplashScreenView) {
iconView = ((SplashScreenView) splashScreenView).getIconView();
brandingView = ((SplashScreenView) splashScreenView).getBrandingView();
}
// 1. fade out 动画
if (iconView != null) {
iconView.setAlpha(iconStartAlpha * (1 - iconProgress));
}
if (brandingView != null) {
brandingView.setAlpha(brandingStartAlpha * (1 - iconProgress));
}
final float revealLinearProgress = getProgress(linearProgress, appRevealDelay,
appRevealDuration, animationDuration);
// 2. radial vanish 动画
radialVanishAnimation.onAnimationProgress(revealLinearProgress);
// 3. shit up 动画
if (finalShiftUpAnimation != null) {
finalShiftUpAnimation.onAnimationProgress(revealLinearProgress);
}
});
return animator;
}
这里有三个动画
- 对启动窗口 View 的 icon view, branding view 做 fade out 动画。
- RadialVanishAnimation 是一个 View,在其构造函数中,RadialVanishAnimation 被添加到 SplashScreenView(继承自FrameLayout)中。RadialVanishAnimation 执行动画,就是对 RadialVanishAnimation 这个 View 做 radial vanish 动画。
- SplashScreenView(继承自FrameLayout)添加 occludeHoleView。ShiftUpAnimation 不仅对 occludeHoleView 做 shift up 动画,还会对真窗 leash 做 shift up 动画。
个人感觉,除了 fade out 动画,其他都是些花里胡哨的东西。对于性不能不好的机器,完全可以只做 fade out 动画即可。
radial vanish 动画,是一个自定义 View 的动画,有基础的读者可以自行分析下。这里来看下 shift up 动画
// SplashScreenExitAnimationUtils.java
public static final class ShiftUpAnimation {
public ShiftUpAnimation(float fromYDelta, float toYDelta, View occludeHoleView,
SurfaceControl firstWindowSurface, ViewGroup splashScreenView,
TransactionPool transactionPool, Rect firstWindowFrame,
int mainWindowShiftLength, float roundedCornerRadius) {
// 0
mFromYDelta = fromYDelta - Math.max(firstWindowFrame.top, roundedCornerRadius);
// -20
mToYDelta = toYDelta;
mOccludeHoleView = occludeHoleView;
mApplier = new SyncRtSurfaceTransactionApplier(occludeHoleView);
// 真窗 leash
mFirstWindowSurface = firstWindowSurface;
mSplashScreenView = splashScreenView;
mTransactionPool = transactionPool;
mFirstWindowFrame = firstWindowFrame;
// 20
mMainWindowShiftLength = mainWindowShiftLength;
}
void onAnimationProgress(float linearProgress) {
if (mFirstWindowSurface == null || !mFirstWindowSurface.isValid()
|| !mSplashScreenView.isAttachedToWindow()) {
return;
}
final float progress = SHIFT_UP_INTERPOLATOR.getInterpolation(linearProgress);
final float dy = mFromYDelta + (mToYDelta - mFromYDelta) * progress;
// mOccludeHoleView 做 transaltate y 动画:0 -> -20
mOccludeHoleView.setTranslationY(dy);
// mTmpTransform 操作的是真窗 leash 的 y: 20 -> 0
mTmpTransform.setTranslate(0 /* dx */, dy);
final SurfaceControl.Transaction tx = mTransactionPool.acquire();
// transaction 绑定当前 Vsync id,表示需要在当前帧处理这个动画
tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
mTmpTransform.postTranslate(mFirstWindowFrame.left,
mFirstWindowFrame.top + mMainWindowShiftLength);
SyncRtSurfaceTransactionApplier.SurfaceParams
params = new SyncRtSurfaceTransactionApplier.SurfaceParams
.Builder(mFirstWindowSurface)
.withMatrix(mTmpTransform)
.withMergeTransaction(tx)
.build();
mApplier.scheduleApply(params);
mTransactionPool.release(tx);
}
}
对真窗 leash 的操作,是通过监听 occludeHoleView 的硬件加速渲染,以达到 leash 动画和 occludeHoleView 同时执行,如下
// SyncRtSurfaceTransactionApplier.java
public void scheduleApply(final SurfaceParams... params) {
if (mTargetViewRootImpl == null) {
return;
}
// mTargetViewRootImpl 是 occludeHoleView
mTargetSc = mTargetViewRootImpl.getSurfaceControl();
// 创建一个 transaction
final Transaction t = new Transaction();
// 通过 transaction 保存要对真窗 leash 执行的操作
applyParams(t, params);
// 监听 occludeHoleView 的硬件渲染
mTargetViewRootImpl.registerRtFrameCallback(frame -> {
if (mTargetSc != null && mTargetSc.isValid()) {
// 执行 transaction
applyTransaction(t, frame);
}
// The transaction was either dropped, successfully applied, or merged with a future
// transaction, so we can safely release its resources.
t.close();
});
// Make sure a frame gets scheduled.
mTargetViewRootImpl.getView().invalidate();
}
void applyTransaction(Transaction t, long frame) {
if (mTargetViewRootImpl != null) {
// t 是对 leash 操作的 transaction,把它与 occludeHoleView 的 transaction 一起执行
mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
} else {
t.apply();
}
}
当所有动画执行完成,执行回调
// SplashScreenExitAnimation.java
public void onAnimationEnd(Animator animation) {
reset();
InteractionJankMonitor.getInstance().end(CUJ_SPLASHSCREEN_EXIT_ANIM);
}
private void reset() {
if (DEBUG_EXIT_ANIMATION) {
Slog.v(TAG, "vanish animation finished");
}
if (mSplashScreenView.isAttachedToWindow()) {
// 把 SplashScreenView 隐藏掉
// 注意,SplashScreenView 并不是启动窗口的 root view,而只是其主要的一部分
mSplashScreenView.setVisibility(GONE);
// mFinishCallback 是 SplashscreenWindowCreator#removeWindowInner()
if (mFinishCallback != null) {
mFinishCallback.run();
mFinishCallback = null;
}
}
}
// SplashscreenWindowCreator.java
// decorView 不是 SlashScreenView,而是它的 parent view,也是启动窗口的 root view
// hideView 为 true
private void removeWindowInner(@NonNull View decorView, boolean hideView) {
requestTopUi(false);
if (!decorView.isAttachedToWindow()) {
return;
}
// 启动窗口 root view 可见性更新为 GONE
// 会导致 WMS 对启动窗口 surface 做 fade out 动画
if (hideView) {
decorView.setVisibility(View.GONE);
}
// 通过 WindowManagerGlobal 移除启动窗口 root view,
// 会导致 WMS 移除整个启动窗口,包括启动窗口 surface
mWindowManagerGlobal.removeView(decorView, false /* immediate */);
}