Android T WMS窗口添加流程其三——服务端代码详解(窗口状态刷新)

1,275 阅读21分钟

2.3 窗口状态刷新

当应用端执行measure-layout-draw之后,便会调用WindowManagerService.finishDrawingWindow,处理Surface的状态变更并将Surface show出来。 首先还是看一下该阶段的流程图,对整个流程有个初步的了解。 将整个流程分为三部分:

1.WMS接受客户端请求,将mDrawState更新为COMMIT_DRAW_PENDING,并请求窗口布局。

2.mDrawState更新为HAS_DRAW,再次请求窗口布局。

3.执行show Surface。

2.3.1 接受客户端请求

代码路径:framework/services/core/java/com/android/server/wm/Session.java

    @Override
    public void finishDrawing(IWindow window,
            @Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
        if (DEBUG) Slog.v(TAG_WM, "IWindow finishDrawing called for " + window);
        //调用WMS中的finishDrawingWindow处理
        mService.finishDrawingWindow(this, window, postDrawTransaction, seqId);
    }

2.3.2 finishDrawingWindow

1.在WMS中根据客户端的Binder在mWindowMap中获取对应的WindowState。

2.调用WindowState.finishDrawing执行mDrawState的状态变更。

3.将WindowState.mLayoutNeeded标志位置为true。

4.请求进行布局刷新。

代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java

    void finishDrawingWindow(Session session, IWindow client,
            @Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
        if (postDrawTransaction != null) {
            postDrawTransaction.sanitize();
        }

        final long origId = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
            	/*1.根据客户端的Binder在mWindowMap中获取对应的WindowState*/
                WindowState win = windowForClientLocked(session, client, false);
                ProtoLog.d(WM_DEBUG_ADD_REMOVE, "finishDrawingWindow: %s mDrawState=%s",
                        win, (win != null ? win.mWinAnimator.drawStateToString() : "null"));
                /*2.finishDrawing执行mDrawState的状态更变*/
                if (win != null && win.finishDrawing(postDrawTransaction, seqId)) {
                    if (win.hasWallpaper()) {
                        win.getDisplayContent().pendingLayoutChanges |=
                                WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
                    }
                    /*3.将DisplayContent中mLayoutNeeded置为true*/
                    //该标志位是判断是否进行窗口大小尺寸计算的条件之一
                    win.setDisplayLayoutNeeded();
                    /*4.请求进行布局刷新*/
                    mWindowPlacerLocked.requestTraversal();
                }
            }
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }
1.mDrawState的状态更变

在finishDrawingWindow中调用WindowState的finishDrawing方法 win.finishDrawing(postDrawTransaction, seqId) 这个方法主要调用了WindowStateAnimator的finishDrawingLocked进行状态更变 代码路径:framework/services/core/java/com/android/server/wm/WindowState.java

   boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction, int syncSeqId) {
   		......
   		//调用WindowStateAnimator.finishDrawingLocked,会将mDrawState的状态更改为COMMIT_DRAW_PENDING
        final boolean layoutNeeded =
                mWinAnimator.finishDrawingLocked(postDrawTransaction, mClientWasDrawingForSync);
        mClientWasDrawingForSync = false;
        // We always want to force a traversal after a finish draw for blast sync.
        return !skipLayout && (hasSyncHandlers || layoutNeeded);
    }

我们继续看看WindowStateAnimator中的finishDrawingLocked()方法 首先判断mDrawState的状态是否为DRAW_PENDING,在我们创建SurfaceControl时,会将mDrawState状态更新为DRAW_PENDING。因此接下来将状态调整为COMMIT_DRAW_PENDING。 代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java

    boolean finishDrawingLocked(SurfaceControl.Transaction postDrawTransaction,
            boolean forceApplyNow) {
        ......
        boolean layoutNeeded = false;

        if (mDrawState == DRAW_PENDING) {
        	......
        	//如果当前状态为DRAW_PENDING,则将mDrawState更变为COMMIT_DRAW_PENDING
            mDrawState = COMMIT_DRAW_PENDING;
            layoutNeeded = true;
        }
        ......
        return layoutNeeded;
    }
2.请求布局刷新

在finishDrawingWindow中请求布局刷新 mWindowPlacerLocked.requestTraversal(); requestTraversal中主要做了两件事:

1.首先将遍历标志为mTraversalSchedule置为true。

2.其次发送handle消息mPerformSurfacePlacement

    void requestTraversal() {
    	//判断遍历标志mTraversalScheduled是否为true
        if (mTraversalScheduled) {
            return;
        }

        // Set as scheduled even the request will be deferred because mDeferredRequests is also
        // increased, then the end of deferring will perform the request.
        //将遍历标志位置为true
        mTraversalScheduled = true;
        if (mDeferDepth > 0) {
            mDeferredRequests++;
            if (DEBUG) Slog.i(TAG, "Defer requestTraversal " + Debug.getCallers(3));
            return;
        }
        //发送handle消息,处理消息会调用mPerformSurfacePlacement
        mService.mAnimationHandler.post(mPerformSurfacePlacement);
    }

mPerformSurfacePlacement会新建一个线程调用performSurfacePlacement。 performSurfacePlacement方法我们在讲relayoutWindow相关流程的时候讲过,这是执行遍历布局的入口。可以回看下,在Android T WMS窗口添加流程其三——服务端代码详解(窗口位置和大小计算)中的【2.2.4 计算窗口大小位置中的“1.处理窗口布局循环”

    private class Traverser implements Runnable {
        @Override
        public void run() {
            synchronized (mService.mGlobalLock) {
            	//调用执行performSurfacePlacement
                performSurfacePlacement();
            }
        }
    }

    private final Traverser mPerformSurfacePlacement = new Traverser();
    
    final void performSurfacePlacement(boolean force) {
    	//当mDeferDepth大于0且force为false时,则将延迟布局请求数+1,并直接返回
        if (mDeferDepth > 0 && !force) {
            mDeferredRequests++;
            return;
        }
        //将循环的最大次数设置为6次
        int loopCount = 6;
        do {
        	//将该标志为设置为false
            mTraversalScheduled = false;
            //执行窗口布局操作
            performSurfacePlacementLoop();
            mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
            loopCount--;
        //只有当mTraversalScheduled为true且循环次数大于0时,才会再次循环执行布局
        } while (mTraversalScheduled && loopCount > 0);
        mService.mRoot.mWallpaperActionPending = false;
    }

    private void performSurfacePlacementLoop() {
    	//若当前已经进行布局操作,则无需重复调用直接返回
        if (mInLayout) {
            ......
            return;
        }
        ......
        //将该标志位置为true,表示正在处于布局过程中
        mInLayout = true;
        ......
        try {
        	/*1.调用RootWindowContainer的performSurfacePlacement()方法对所有窗口执行布局操作*/
            mService.mRoot.performSurfacePlacement();

            mInLayout = false;

            //判断每个(DisplayContent)屏幕的mLayoutNeeded标志位是否为true
            if (mService.mRoot.isLayoutNeeded()) {
            	/*2.若需要布局,且布局次数小于6次,则需要再次请求布局*/
                if (++mLayoutRepeatCount < 6) {
                	//该方法中会将mTraversalScheduled标志位设置位true
                    requestTraversal();
                } else {
                    Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
                    mLayoutRepeatCount = 0;
                }
            } else {
                mLayoutRepeatCount = 0;
            }

            if (mService.mWindowsChanged && !mService.mWindowChangeListeners.isEmpty()) {
                mService.mH.removeMessages(REPORT_WINDOWS_CHANGE);
                mService.mH.sendEmptyMessage(REPORT_WINDOWS_CHANGE);
            }
        } catch (RuntimeException e) {
            mInLayout = false;
            Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
        }
    }

代码路径:framework/services/core/java/com/android/server/wm/RootWindowContainer.java

    void performSurfacePlacement() {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
        try {
        	//调用performSurfacePlacementNoTrace()
            performSurfacePlacementNoTrace();
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

    // "Something has changed!  Let's make it correct now."
    // TODO: Super long method that should be broken down...
    void performSurfacePlacementNoTrace() {
    	......
    	/*1.如果有焦点变化,更新焦点*/
        if (mWmService.mFocusMayChange) {
            mWmService.mFocusMayChange = false;
            mWmService.updateFocusedWindowLocked(
                    UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
        }
        ......
        
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
        //开启事务,获取GlobalTransactionWrapper对象
        mWmService.openSurfaceTransaction();
        try {
        	/*2.执行窗口尺寸计算,surface状态变更等操作*/
            applySurfaceChangesTransaction();
        } catch (RuntimeException e) {
            Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
        } finally {
        	//关闭事务,把事务提交
            mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            if (SHOW_LIGHT_TRANSACTIONS) {
                Slog.i(TAG,
                        "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
            }
        }
		......
		/*3.将Surface状态变更为HAS_DRAWN,触发App触发动画。该过程在“2.3.3mDrawState变更为HAS_DRAW”流程中再详细分析*/
        checkAppTransitionReady(surfacePlacer);
        ......
        /*4.遍历所有DisplayContent,如果壁纸有变化,更新壁纸*/
        for (int displayNdx = 0; displayNdx < mChildren.size(); ++displayNdx) {
            final DisplayContent displayContent = mChildren.get(displayNdx);
            //判断DisplayContent的壁纸是否需要改变
            if (displayContent.mWallpaperMayChange) {
                ProtoLog.v(WM_DEBUG_WALLPAPER, "Wallpaper may change!  Adjusting");
                displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                if (DEBUG_LAYOUT_REPEATS) {
                    surfacePlacer.debugLayoutRepeats("WallpaperMayChange",
                            displayContent.pendingLayoutChanges);
                }
            }
        }
        /*5.在此处理焦点变化*/
        if (mWmService.mFocusMayChange) {
            mWmService.mFocusMayChange = false;
            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
                    false /*updateInputWindows*/);
        }
        ......
        /*6.如果过程中size或者位置变化,则通知客户端重新relayout*/
        handleResizingWindows();

        if (mWmService.mDisplayFrozen) {
            ProtoLog.v(WM_DEBUG_ORIENTATION,
                    "With display frozen, orientationChangeComplete=%b",
                    mOrientationChangeComplete);
        }
        if (mOrientationChangeComplete) {
            if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
                mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
                mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
                mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
            }
            mWmService.stopFreezingDisplayLocked();
        }

        // Destroy the surface of any windows that are no longer visible.
        /*7.销毁不可见的窗口*/
        i = mWmService.mDestroySurface.size();
        if (i > 0) {
            do {
                i--;
                WindowState win = mWmService.mDestroySurface.get(i);
                win.mDestroying = false;
                final DisplayContent displayContent = win.getDisplayContent();
                if (displayContent.mInputMethodWindow == win) {
                    displayContent.setInputMethodWindowLocked(null);
                }
                if (displayContent.mWallpaperController.isWallpaperTarget(win)) {
                    displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
                }
                win.destroySurfaceUnchecked();
            } while (i > 0);
            mWmService.mDestroySurface.clear();
        }
        ......
    }

这里我们主要关注applySurfaceChangesTransaction();checkAppTransitionReady(surfacePlacer);

* 窗口位置计算与窗口状态刷新流程不同点

可以发现,窗口位置计算流程与窗口状态刷新流程都调用了performSurfacePlacement,两次调用的主要不同点在于:

1.窗口状态刷新流程在DisplayContent.applySurfaceChangesTransaction中调用mApplySurfaceChangesTransaction,处理mDrawState状态。

2.窗口状态刷新流程在RootWindowContainer.performSurfacePlacementNoTrace中调用checkAppTransitionReady,处理mDrawState状态变更为HAS_DRAWN,触发Activity过渡动画。

3.窗口状态刷新流程在WindowSurfacePlacementLoop.performSurfacePlacementLoop中会调用requestTraversal,请求再次布局。

4.窗口状态刷新流程在mDrawState状态变更为HAS_DRAWN后,会通过WindowStateAnimator.prepareSurfaceLocked处理surface的位置、大小以及显示等。

2.3.3 mDrawState变更为HAS_DRAW

1.mApplySurfaceChangesTransaction

RootWindowContainer的applySurfaceChangesTransaction()方法最终会调用到DisplayContent中调用的applySurfaceChangesTransaction()方法(在Android T WMS窗口添加流程其三——服务端代码详解(窗口位置和大小计算)中的【2.2.4 计算窗口大小位置】中讲过流程,不再赘述) 我们接着该方法中的mApplySurfaceChangesTransaction跟踪。 forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */); 如果当前WindowState存在surfaceControl,则进入到WindowStateAnimator进行mDrawState的状态更变。 代码路径:framework/services/core/java/com/android/server/wm/DisplayContent.java

    private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
	    ......
        //首先判断当前windowState的是否有surfaceControl
        if (w.mHasSurface) {
            // Take care of the window being ready to display.
            //调用WindowStateAnimator的commitFinishDrawingLocked()方法
            final boolean committed = winAnimator.commitFinishDrawingLocked();
            ......
        }
        ......
    };

继续看看WindowStateAnimator的commitFinishDrawingLocked()方法 final boolean committed = winAnimator.commitFinishDrawingLocked();

1.对mDrawState的状态进行过滤,非COMMIT_DRAW_PENDING和READY_TO_SHOW则直接返回。

2.此时我们的mDrawState已经在“【2.3.2 finishDrawingWindow】”将状态更新为COMMIT_DRAW_PENDING,因此此处将其变更为READY_TO_SHOW。

代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java

    // This must be called while inside a transaction.
    boolean commitFinishDrawingLocked() {
    	//非COMMIT_DRAW_PENDING和READY_TO_SHOW则直接返回
        if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {
            return false;
        }
        ProtoLog.i(WM_DEBUG_ANIM, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW %s",
                mSurfaceController);
        //将状态更变为READY_TO_SHOW
        mDrawState = READY_TO_SHOW;
        boolean result = false;
        final ActivityRecord activity = mWin.mActivityRecord;
        //直接进入到WindowState.performShowLocked()流程的三种情况
        //1.如果ActivityRecord为空,这种情况可以理解为不依赖Activity的窗口,比如常见的悬浮窗
        //2.或者canShowWindows()为true,这个方法大概是说:只有当所有窗口都已绘制完成,并且没有正在进行父级窗口的应用过渡动画,并且没有非默认颜色的窗口存在时,返回true
        //3.或者窗口类型为启动窗口,启动窗口就是StartingWindow,应用启动时出现的窗口,常见的就是Splash screen ,许多应用都会定义自己的SplashActivity
        //进入performShowLocked()流程后mDrawState更新HAS_DRAWN
        //由于非这三种情况最终也会调用到performShowLocked(),因此下面这种情况我们暂不讨论
        if (activity == null || activity.canShowWindows()
                || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {
            result = mWin.performShowLocked();
        }
        return result;
    }
2.checkAppTransitionReady()

这里我们继续跟踪RootWindowContainer.performSurfacePlacementNoTrace()方法中的checkAppTransitionReady()方法 checkAppTransitionReady(surfacePlacer); 该方法会遍历所有DisplayContent,处理activity的过滤动画,此处我们只有跟踪有关mDrawState状态更变的相关代码 代码路径:framework/services/core/java/com/android/server/wm/RootWindowContainer.java

    private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
        // Trace all displays app transition by Z-order for pending layout change.
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final DisplayContent curDisplay = mChildren.get(i);

            // If we are ready to perform an app transition, check through all of the app tokens
            // to be shown and see if they are ready to go.
            //检查所有要显示的app token,是否已经准备就绪
            if (curDisplay.mAppTransition.isReady()) {
                // handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.
                curDisplay.mAppTransitionController.handleAppTransitionReady();
                if (DEBUG_LAYOUT_REPEATS) {
                    surfacePlacer.debugLayoutRepeats("after handleAppTransitionReady",
                            curDisplay.pendingLayoutChanges);
                }
            }
            ......
        }
    }

调用AppTransitionController的handleAppTransitionReady()方法,该方法主要做了以下事情

1.处理activity的过渡动画(远程动画)

2.分别调用 handleClosingApps以及handleOpeningApps对要关闭的和要打开的Activity进行可见性更新。

3.调用AppTransition.goodToGo方法走播放远程动画流程。

4.调用onTransitionStarting方法开始快照相关流程。详细流程见:TaskSnapshot创建和移除流程

5.由于activity的可见性变更,将DisplayContent.mLayoutNeeded设置为true,该标志位在DisplayContent.performLayoutNoTrace中用来判断是否对当前DisplayContent下的所有窗口进行刷新。

代码路径:framework/services/core/java/com/android/server/wm/AppTransitionController.java

    /**
     * Handle application transition for given display.
     */
    void handleAppTransitionReady() {
        ......
        //通过getTransitCompatType方法获取transit的值
        @TransitionOldType final int transit = getTransitCompatType(
                mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps,
                mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers,
                mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
                mDisplayContent.mSkipAppTransitionAnimation);
    	......
    	//获取正在打开 (mOpeningApps)、关闭 (mClosingApps) 和切换 (mChangingContainers) 的应用的activity类型
    	//并将它们存储在 activityTypes 集合中。
    	final ArraySet<Integer> activityTypes = collectActivityTypes(mDisplayContent.mOpeningApps,
                mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers);
        //被用于查找与给定transit和activityTypes相关的 ActivityRecord
        //也就是我们当前打开的应用的ActivityRecord
        final ActivityRecord animLpActivity = findAnimLayoutParamsToken(transit, activityTypes,
                mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
                mDisplayContent.mChangingContainers);
        //获取正在打开的应用列表 (mOpeningApps) 中的顶层应用。
        //ignoreHidden 参数设置为 false,意味着即使应用是隐藏的,也会被考虑在内
        final ActivityRecord topOpeningApp =
                getTopApp(mDisplayContent.mOpeningApps, false /* ignoreHidden */);
        //获取正在关闭的应用列表 (mClosingApps) 中的顶层应用
        final ActivityRecord topClosingApp =
                getTopApp(mDisplayContent.mClosingApps, false /* ignoreHidden */);
        //获取正在切换的应用列表 (mChangingContainers) 中的顶层应用
        //其取决于参数DisplayContent.mChangingContainers中是否有值
        /** 
        有三种情况会给DisplayContent.mChangingContainers中添加值
        1.{@link Task}在全屏和浮窗之间发生切换
        2.{@link TaskFragment}已组织好并且正在更改窗口边界
        3.{@link ActivityRecord}被重新分配到一个有组织的{@link TaskFragment}中
        **/
        final ActivityRecord topChangingApp =
                getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
        //从之前找到的animLpActivity(正在打开的应用的ActivityRecord)的窗口中获取布局参数
        final WindowManager.LayoutParams animLp = getAnimLp(animLpActivity);
    	......
        try {
            /*1.1应用app transition动画(远程动画)*/
            applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
                    animLp, voiceInteraction);
            /*1.2.1处理closing activity可见性*/
            handleClosingApps();
            /*1.2.2处理opening actvity可见性*/
            handleOpeningApps();
            //处理用于处理正在切换的应用
            handleChangingApps(transit);
            //处理正在关闭或更改的容器
            handleClosingChangingContainers();

            //设置与最后一次应用过渡动画相关的信息
            appTransition.setLastAppTransition(transit, topOpeningApp,
                    topClosingApp, topChangingApp);

            final int flags = appTransition.getTransitFlags();
            /*1.3播放远程动画*/
            layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
            //处理非应用窗口的过渡动画
            handleNonAppWindowsInTransition(transit, flags);
            //执行动画回调
            appTransition.postAnimationCallback()
        } finally {
            mService.mSurfaceAnimationRunner.continueStartingAnimations();
        }
        /*1.4 开始快照相关流程*/
        mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);

        mDisplayContent.mOpeningApps.clear();
        mDisplayContent.mClosingApps.clear();
        mDisplayContent.mChangingContainers.clear();
        mDisplayContent.mUnknownAppVisibilityController.clear();
        mDisplayContent.mClosingChangingContainers.clear();

        // This has changed the visibility of windows, so perform
        // a new layout to get them all up-to-date.
        /*2.由于activity的可见性变更,将DisplayContent.mLayoutNeeded标志位置为true*/
        mDisplayContent.setLayoutNeeded();
        ......
    }

下面我们来讲下这个方法里面的主要方法

applyAnimations()

applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, animLp, voiceInteraction); 基于一组ActivityRecord来应用动画,这些ActivityRecord表示正在进行切换的应用。

mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps这两个参数分别代表正在打开和关闭的应用;

transit通过前面getTransitCompatType方法中获取,是TRANSIT_OLD_WALLPAPER_CLOSE(12);

animLp通过前面getAnimLp方法中获取,用于定义窗口的布局参数。这里就是代表正在打开的应用的ActivityRecord的窗口布局参数;

voiceInteraction:表示是否为语音交互。

    /**
     * Apply an app transition animation based on a set of {@link ActivityRecord}
     *
     * @param openingApps The list of opening apps to which an app transition animation applies.
     * @param closingApps The list of closing apps to which an app transition animation applies.
     * @param transit The current transition type.
     * @param animLp Layout parameters in which an app transition animation runs.
     * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
     *                         interaction session driving task.
     */
    private void applyAnimations(ArraySet<ActivityRecord> openingApps,
            ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit,
            LayoutParams animLp, boolean voiceInteraction) {
        //方法检查过渡类型是否未设置,或者打开和关闭的应用程序是否都为空。如果是,则方法直接返回,不执行任何动画。
        if (transit == WindowManager.TRANSIT_OLD_UNSET
                || (openingApps.isEmpty() && closingApps.isEmpty())) {
            return;
        }

        //调用getAnimationTargets方法获取打开和关闭的窗口容器(WindowContainer)
        final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
                openingApps, closingApps, true /* visible */);
        final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
                openingApps, closingApps, false /* visible */);
        //打开和关闭的窗口应用动画。这是通过调重载的applyAnimations方法完成的,传递相应的参数,如动画的目标、过渡类型等。
        applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
                voiceInteraction);
        applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
                voiceInteraction);
        //如果存在最近任务动画控制器(RecentsAnimationController),则发送任务出现任务
        final RecentsAnimationController rac = mService.getRecentsAnimationController();
        if (rac != null) {
            rac.sendTasksAppeared();
        }

        //遍历打开和关闭的应用,并设置mOverrideTaskTransition为false
        for (int i = 0; i < openingApps.size(); ++i) {
            openingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
        }
        for (int i = 0; i < closingApps.size(); ++i) {
            closingApps.valueAtUnchecked(i).mOverrideTaskTransition = false;
        }

        //如果存在辅助功能控制器(AccessibilityController)且有回调,则调用其onAppWindowTransition方法。
        final AccessibilityController accessibilityController =
                mDisplayContent.mWmService.mAccessibilityController;
        if (accessibilityController.hasCallbacks()) {
            accessibilityController.onAppWindowTransition(mDisplayContent.getDisplayId(), transit);
        }
    }

传递关键参数,处理应用程序窗口的打开和关闭动画。 通过getAnimationTargets方法获取当前打开和关闭的应用的容器,即ActivityRecord的容器。 最关键的方法是调用的applyAnimations方法:

        applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
                voiceInteraction);
        applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
                voiceInteraction);

我们这里openingWcsclosingWcs实际上表示的是应用的容器,即Task;openingAppsclosingApps就是前面传递的mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,分别代表正在打开和关闭的应用,也是挂在对应Task下面的ActivityRecord。并且传递了应用的可见性visible,true可见,false不可见。 比如,在我们桌面点击打开应用的流程中,openingWcs实际上指的是应用的Task,openingApps是应用的ActivityRecord(其实就是应用的主界面),其可见性为true;closingWcs对应的是桌面的Task,closingApps是桌面的ActivityRecord,其可见性为false。

注: 从这里开始后续流程执行了两次,第一次是打开的应用流程,第二次是关闭的应用流程(一个应用的启动,伴随这另一个应用的退出,浮窗等特殊场景除外)。 从桌面点击开启应用的场景来说,一次是启动的应用角度执行流程,另一次是桌面角度执行流程。 从代码逻辑上来说,唯一的不同点是传递的可见性的值不同。

这是调用的是重载的applyAnimations方法

	  /**
     * Apply animation to the set of window containers.
     *
     * @param wcs The list of {@link WindowContainer}s to which an app transition animation applies.
     * @param apps The list of {@link ActivityRecord}s being transitioning.
     * @param transit The current transition type.
     * @param visible {@code true} if the apps becomes visible, {@code false} if the apps becomes
     *                invisible.
     * @param animLp Layout parameters in which an app transition animation runs.
     * @param voiceInteraction {@code true} if one of the apps in this transition belongs to a voice
     *                         interaction session driving task.
     */
    private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,
            @TransitionOldType int transit, boolean visible, LayoutParams animLp,
            boolean voiceInteraction) {
        //获取窗口容器的数量
        final int wcsCount = wcs.size();
        //遍历每一个应用的窗口容器
        for (int i = 0; i < wcsCount; i++) {
            final WindowContainer wc = wcs.valueAt(i);
            // If app transition animation target is promoted to higher level, SurfaceAnimator
            // triggers WC#onAnimationFinished only on the promoted target. So we need to take care
            // of triggering AR#onAnimationFinished on each ActivityRecord which is a part of the
            // app transition.
            //对于每一个应用的窗口容器,检查正在进行切换的应用(apps)中哪些是该窗口容器的后代。
            //就比如应用的ActivityRecord是是应用的Task的后代
            final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
            for (int j = 0; j < apps.size(); ++j) {
                final ActivityRecord app = apps.valueAt(j);
                //app如果是wc的后代,将其添加到一个列表中。
                if (app.isDescendantOf(wc)) {
                    transitioningDescendants.add(app);
                }
            }
            //调用每个应用的窗口容器的applyAnimation方法,传入相应的参数
            //这些参数包含动画的布局、过渡类型、是否可见、是否有语音交互以及需要做动画的ActivityRecord应用的列表。
            wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
        }
    }

入参含义: wcs: 一个WindowContainer对象的集合,这些对象是需要应用动画的窗口容器。 apps: 一个ActivityRecord对象的集合,这些对象表示正在进行切换的应用程序。 transit: 当前的过渡类型,例如淡入淡出、滑动等。 visible: 一个布尔值,表示应用是否变为可见。 animLp: 布局参数,定义了动画运行时的布局。 voiceInteraction: 一个布尔值,表示是否有语音交互。

关键代码解读:

  • final WindowContainer wc = wcs.valueAt(i);获取窗口容器 wcs是前面传递过来的是Task,wc就是依次获取当前应用的Task。 比如,在我们桌面点击打开应用的流程中,就是获取了应用的Task和桌面的Task。

  • transitioningDescendants存储的就是需要做动画的ActivityRecord。

  • 传递动画参数 通过wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);方法,传递参数动画的布局、过渡类型、是否可见、是否有语音交互以及需要做动画的ActivityRecord应用的列表。 wc就是Task,其没有applyAnimation方法,向上找父类WindowContainer.applyAnimation方法调用。

这部分远程动画流程,这里不做重点,详细流程见 远程动画流程

handleClosingApps() 该方法中主要的作用就是将所有即将close的activity的mVisible标志设置为false。该标志位在后续prepareSurfaces中是判断是否show surface的条件之一。

   private void handleClosingApps() {
        final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
        final int appsCount = closingApps.size();

        for (int i = 0; i < appsCount; i++) {
            final ActivityRecord app = closingApps.valueAt(i);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", app);
            //设置activity的可见性,将mVisible设置为false
            app.commitVisibility(false /* visible */, false /* performLayout */);
            app.updateReportedVisibilityLocked();
            // Force the allDrawn flag, because we want to start
            // this guy's animations regardless of whether it's
            // gotten drawn.
            //强制将allDrawn设置为true
            app.allDrawn = true;
            ......
        }
    }

handleOpeningApps() 该方法与handleClosingApps方法类似,主要处理两件事情:

1.将所有即将open的activity的mVisible标志位设置为true.

2.调用ActivityRecord.showAllWindowsLocked(),最终会调用到WindowState.performShowLocked() ,处理mDrawState的状态变更

    private void handleOpeningApps() {
        final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
        final int appsCount = openingApps.size();

        for (int i = 0; i < appsCount; i++) {
            final ActivityRecord app = openingApps.valueAt(i);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app);
            /*1.设置activity的可见性,将mVisible设置为true*/
            app.commitVisibility(true /* visible */, false /* performLayout */);
            ......
            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                    ">>> OPEN TRANSACTION handleAppTransitionReady()");
            //开启事务
            mService.openSurfaceTransaction();
            try {
            	/*2.此方法最终会调用到WindowState.performShowLocked*/
                app.showAllWindowsLocked();
            } finally {
			//关闭事务
            mService.closeSurfaceTransaction("handleAppTransitionReady");
                if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                        "<<< CLOSE TRANSACTION handleAppTransitionReady()");
            }
            ......
        }
    }

app.showAllWindowsLocked();先调用到ActivityRecord的showAllWindowsLocked() 代码路径:framework/services/core/java/com/android/server/wm/ActivityRecord.java

    /**
     * This must be called while inside a transaction.
     */
    void showAllWindowsLocked() {
        forAllWindows(windowState -> {
            if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + windowState);
            windowState.performShowLocked();
        }, false /* traverseTopToBottom */);
    }

windowState.performShowLocked();再调用到WindowState的performShowLocked() 将mDrawState的状态由READY_TO_SHOW变更为HAS_DRAW 代码路径:framework/services/core/java/com/android/server/wm/WindowState.java

    // This must be called while inside a transaction.
    boolean performShowLocked() {
    	......
		//获取WindowStateAnimator.mDrawState
        final int drawState = mWinAnimator.mDrawState;
        //这里判断(drawState 状态为HAS_DRAWN 或者READY_TO_SHOW)且ActivityRecord不为空
        if ((drawState == HAS_DRAWN || drawState == READY_TO_SHOW) && mActivityRecord != null) {
        	//窗口类型不为启动窗口
            if (mAttrs.type != TYPE_APPLICATION_STARTING) {
                mActivityRecord.onFirstWindowDrawn(this);
            } else {
                mActivityRecord.onStartingWindowDrawn();
            }
        }
		//如果当前mDrawState的状态不为READY_TO_SHOW ,则直接返回
        if (mWinAnimator.mDrawState != READY_TO_SHOW || !isReadyForDisplay()) {
            return false;
        }
        ......
        //走入窗口动画流程
        mWinAnimator.applyEnterAnimationLocked();
        
        // Force the show in the next prepareSurfaceLocked() call.
        mWinAnimator.mLastAlpha = -1;
        ProtoLog.v(WM_DEBUG_ANIM, "performShowLocked: mDrawState=HAS_DRAWN in %s", this);
        //设置mDrawState的状态为HAS_DRAWN
        mWinAnimator.mDrawState = HAS_DRAWN;
        mWmService.scheduleAnimationLocked();
        ......
        return true;
    }

添加窗口时,会调用这个mWinAnimator.applyEnterAnimationLocked();窗口动画的方法,然后调用到applyAnimationLocked();窗口移除时,会直接调用applyAnimationLocked()显示动画。 后续动画相关流程可参考 本地动画流程

TaskSnapshot快照流程 mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);Android T TaskSnapshot创建和移除流程

3.再次请求布局

回到WindowSurfacePlacer中通过requestTraversal(),再次请求布局,该方法将mTraversalScheduled标志位设置为true的判断条件有两个:

1.遍历所有DisplayContent.mLayoutNeeded标志为是否为true。(由于AppTransitionController.handleAppTransitionReady阶段已经将mLayoutNeeded置为true,因此该条件为真)

2.重复布局的次数不能超过6次,该条件也为真。(因为当前还只是第一次布局)

代码路径:framework/services/core/java/com/android/server/wm/WindowSurfacePlacer.java

    private void performSurfacePlacementLoop() {
    	......
        try {
        	......
        	/*1.遍历所有DisplayContent.mLayoutNeeded标志位是否为true*/
            if (mService.mRoot.isLayoutNeeded()) {
            	/*2.如果需要布局,且布局次数小于6次,则需要再次请求布局*/
                if (++mLayoutRepeatCount < 6) {
                	//该方法中会将mTraversalScheduled标志位设置位true
                    requestTraversal();
                } else {
                    Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
                    mLayoutRepeatCount = 0;
                }
            } else {
                mLayoutRepeatCount = 0;
            }
            ......
        } catch (RuntimeException e) {
        ......
        }
    }

接下来进入第二次布局循环,其主要目的是为了show surface

2.3.4 show Surface

在第二次循环中,我们主要关注DisplayContent中applySurfaceChangesTransaction()方法调用的prepareSurfaces()

该方法最终会调用到根容器WindowContainer,来遍历所有子容器中的prepareSurfaces。 代码路径:framework/services/core/java/com/android/server/wm/DisplayContent.java

    @Override
    void prepareSurfaces() {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
        try {
        	//获取事务
            final Transaction transaction = getPendingTransaction();
            //调用其父类方法
            super.prepareSurfaces();

            // TODO: Once we totally eliminate global transaction we will pass transaction in here
            //       rather than merging to global.
            SurfaceControl.mergeToGlobalTransaction(transaction);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

调用其父类方法super.prepareSurfaces(); DisplayContent的父类为WindowContainer 代码路径:framework/services/core/java/com/android/server/wm/WindowContainer.java

    void prepareSurfaces() {
        // If a leash has been set when the transaction was committed, then the leash reparent has
        // been committed.
        mCommittedReparentToAnimationLeash = mSurfaceAnimator.hasLeash();
        //调用所有子容器中的prepareSurfaces
        for (int i = 0; i < mChildren.size(); i++) {
            mChildren.get(i).prepareSurfaces();
        }
    }

mChildren.get(i).prepareSurfaces();在WindowState.prepareSurfaces中,主要做了两方面工作。

1.将mWindowFrames中计算出来的left以及top设置surface位置,并调整窗口比例。

2.控制surface的可见性,查看WindowStateAnimator.prepareSurfaceLocked

代码路径:framework/services/core/java/com/android/server/wm/WindowState.java

    void prepareSurfaces() {
        mIsDimming = false;
        applyDims();
        //实际调用的是其父类WindowContainer的方法
        /*1.最终调用自身的updateSurfacePosition()(自身有重写该方法)计算surface的位置*/
        updateSurfacePositionNonOrganized();
        // Send information to SurfaceFlinger about the priority of the current window.
        updateFrameRateSelectionPriorityIfNeeded();
        //更新窗口比例
        updateScaleIfNeeded();

		/*2.控制surface的可见性,调用WindowStateAnimator的prepareSurfaceLocked()方法*/
        mWinAnimator.prepareSurfaceLocked(getSyncTransaction());
        super.prepareSurfaces();
    }
    
    @Override
    @VisibleForTesting
    void updateSurfacePosition(Transaction t) {
        if (mSurfaceControl == null) {
            return;
        }

        //这段代码首先检查布局是否被延迟(通过 isLayoutDeferred() 方法)
        //或者应用是否正在进行布局(通过 isGoneForLayout() 方法)。
        //如果满足这些条件并且 mSurfacePlacementNeeded 为 false,则方法返回,不执行后续操作。
        //这是因为当布局被延迟或应用正在进行布局时,界面的位置可能不是最新的,因此不执行updateSurfacePosition。
        if ((mWmService.mWindowPlacerLocked.isLayoutDeferred() || isGoneForLayout())
                && !mSurfacePlacementNeeded) {
            // Since this relies on mWindowFrames, changes made while layout is deferred are
            // likely to be invalid. Similarly, if it's goneForLayout, mWindowFrames may not be
            // up-to-date and thus can't be relied on.
            return;
        }
        
        //将mSurfacePlacementNeeded设置为false
        mSurfacePlacementNeeded = false;
        //将mSurfacePosition的left以及top设置mWindowFrames中计算出来的left以及top,并根据parent进行偏移
        transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
                mSurfacePosition);
		//根据壁纸的比例对SurfacePosition进行调整
        if (mWallpaperScale != 1f) {
            final Rect bounds = getLastReportedBounds();
            Matrix matrix = mTmpMatrix;
            matrix.setTranslate(mXOffset, mYOffset);
            matrix.postScale(mWallpaperScale, mWallpaperScale, bounds.exactCenterX(),
                    bounds.exactCenterY());
            matrix.getValues(mTmpMatrixArray);
            mSurfacePosition.offset(Math.round(mTmpMatrixArray[Matrix.MTRANS_X]),
                Math.round(mTmpMatrixArray[Matrix.MTRANS_Y]));
        } else {
            mSurfacePosition.offset(mXOffset, mYOffset);
        }
        ......
    }

mWinAnimator.prepareSurfaceLocked(getSyncTransaction()); 调用WindowStateAnimator的prepareSurfaceLocked()方法,该则真正的处理触发surface show的逻辑。主要分为两部分。

1.将计算的alpha应用于当前surface。

2.判断是否调用showSurfaceRobustlyLocked将surface show出来。

代码路径:framework/services/core/java/com/android/server/wm/WindowStateAnimator.java

   void prepareSurfaceLocked(SurfaceControl.Transaction t) {
        final WindowState w = mWin;
        //首先判断是否有SurfaceControl
        if (!hasSurface()) {
        	......
            return;
        }
		//设置mShowAlpha
        computeShownFrameLocked();

		//判断parentWindow是否hidden,或者当前窗口是否on-screen
        if (w.isParentWindowHidden() || !w.isOnScreen()) {
        	......
        } else if (mLastAlpha != mShownAlpha
                || mLastHidden) {
            mLastAlpha = mShownAlpha;
            ProtoLog.i(WM_SHOW_TRANSACTIONS,
                    "SURFACE controller=%s alpha=%f HScale=%f, VScale=%f: %s",
                    mSurfaceController, mShownAlpha, w.mHScale, w.mVScale, w);
            /*1.设置surface的alpha*/
            boolean prepared =
                mSurfaceController.prepareToShowInTransaction(t, mShownAlpha);
            //如果当前状态为HAS_DRAWN
            if (prepared && mDrawState == HAS_DRAWN) {
                if (mLastHidden) {
                /*2.触发show surface*/
                    if (showSurfaceRobustlyLocked(t)) {
                        mAnimator.requestRemovalOfReplacedWindows(w);
                        //设置mLastHidden为false
                        mLastHidden = false;
                        .......
                    } else {
                        w.setOrientationChanging(false);
                    }
                }
            }
        } else {
            if (mWin.isAnimating(TRANSITION | PARENTS)) {
                ProtoLog.v(WM_DEBUG_ANIM, "prepareSurface: No changes in animation for %s", this);
            }
        }

		......
    }

从上述代码中可以看出触发showSurfaceRobustlyLocked的判断条件有以下几点: 1.w.isParentWindowHidden判断其parent的mHidden是否为true,此时当前窗口没有parent直接返回false

2.w.isOnScreen,判断当前窗口是否在屏幕上,如果该窗口mVisible为true或者在不可见之前正在运行动画,判断为在屏幕上。我们在上次布局的AppTransitionController.handleAppTransitionReady阶段将当前窗口的mVisible置为了true,因此w.isOnScreen返回true。

3.mLastAlpha != mShownAlpha以及mLastHidden满足其一即可,此处我们分析mLastHidden,该标志位在创建SurfaceControl或者hide surface时会被置为true,因为当前窗口才刚刚被创建,因此mLastHidden为true。

经过以上判断可以得出我们顺利触发showSurfaceRobustlyLocked 后面通过WindowStateAnimator将show SurfaceControl的请求传递给了WindowSurfaceController

    /**
     * Have the surface flinger show a surface, robustly dealing with
     * error conditions.  In particular, if there is not enough memory
     * to show the surface, then we will try to get rid of other surfaces
     * in order to succeed.
     *
     * @return Returns true if the surface was successfully shown.
     */
    private boolean showSurfaceRobustlyLocked(SurfaceControl.Transaction t) {
    	//WindowStateAnimator将show SurfaceControl的请求传递给了WindowSurfaceController
    	//调用WindowSurfaceController的showRobustly方法
        boolean shown = mSurfaceController.showRobustly(t);
        //如果没有成功返回false
        if (!shown)
            return false;

        t.merge(mPostDrawTransaction);
        return true;
    }

在WindowSurfaceController中,首先判断标志位mSurfaceShown,若为true则直接返回;若为false,则将mSurfaceShown置为true,并调用SurfaceControl.show。至此真正的绘图已经显示出来,但是否真正的被用户看见,还需要看其parent是否被show。 代码路径:framework/services/core/java/com/android/server/wm/WindowSurfaceController.java

    boolean showRobustly(SurfaceControl.Transaction t) {
    	......

		//首先判断surface是否已经shown
        if (mSurfaceShown) {
            return true;
        }
		//将mSurfaceShown设置为true
        setShown(true);
        //调用SurfceControl中的show方法,将surface show出来
        t.show(mSurfaceControl);
        if (mAnimator.mIsWallpaper) {
            EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE,
                    mAnimator.mWin.getDisplayId(), 1 /* request shown */);
        }
        return true;
    }

从SurfaceControl的创建以及show的流程上看,可以发现WMS是通过WindowSurfaceController对SurfaceControl进行管理的。 最后我们看一下SurfaceControl中的show方法 代码路径:frameworks/base/core/java/android/view/SurfaceControl.java

        /**
         * Request that a given surface and it's sub-tree be shown.
         *
         * @param sc The surface to show.
         * @return This transaction.
         * @hide
         */
        @UnsupportedAppUsage
        public Transaction show(SurfaceControl sc) {
            checkPreconditions(sc);
            nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN);
            return this;
        }