Android T 远程动画显示流程其二——动画的添加流程(更新中)

news2024/12/23 9:18:54

前言

接着上篇文章分析
Android T 远程动画显示流程其一

切入点——处理应用的显示过渡

下面,我们以从桌面点击一个应用启动的场景来分析远程动画的流程,窗口添加的流程见Android T WMS窗口相关流程
这里我们从AppTransitionController.handleAppTransitionReady方法开始跟踪代码流程

代码路径: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
        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) 中的顶层应用
        final ActivityRecord topChangingApp =
                getTopApp(mDisplayContent.mChangingContainers, false /* ignoreHidden */);
        //从之前找到的animLpActivity的窗口中获取布局参数
        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();
        }
        ......

        // 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();
        ......
    }

这个方法主要处理这三件事:
1.处理activity的过渡动画(远程动画)
2.分别调用 handleClosingApps以及handleOpeningApps对要关闭的和要打开的Activity进行可见性更新。
3.调用AppTransition.goodToGo方法走播放远程动画流程。
4.由于activity的可见性变更,将DisplayContent.mLayoutNeeded设置为true,该标志位在DisplayContent.performLayoutNoTrace中用来判断是否对当前DisplayContent下的所有窗口进行刷新。
这里我们主要关注远程动画的流程,主要分为两个部分。

  1. 处理并创建远程动画流程
    applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, animLp, voiceInteraction);
  2. 播放显示远程动画流程
    layoutRedo = appTransition.goodToGo(transit, topOpeningApp);

动画创建流程代码分析

applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit, animLp, voiceInteraction);
基于一组ActivityRecord来应用动画,这些ActivityRecord表示正在进行切换的应用。
mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps这两个参数分别代表正在打开和关闭的应用;
transit通过前面getTransitCompatType方法中获取,是TRANSIT_OLD_WALLPAPER_CLOSE(12);
animLp用于定义窗口的布局参数;
voiceInteraction:表示是否为语音交互

处理并创建远程动画

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

    /**
     * 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);
        }
    }

入参就是前面传递值,含义为:
openingAppsclosingApps: 这两个参数是ActivityRecord类型的数组,分别表示正在打开和关闭的应用,即这些ActivityRecord表示正在进行切换的应用。
transit: 这是一个整型参数,表示过渡类型。例如,WindowManager.TRANSIT_OLD_UNSET表示没有特定的过渡类型。
animLp: 这是一个LayoutParams对象,用于定义窗口的布局参数。
voiceInteraction: 一个布尔值,表示是否为语音交互。

代码解读:
该方法主要负责处理应用程序窗口的打开和关闭动画。它确保应用程序在切换时有一个平滑的视觉效果,为用户提供更好的体验。最后还与辅助功能服务交互,确保辅助功能用户也能正确地感知应用程序窗口的切换。

通过getAnimationTargets方法获取当前打开和关闭的应用的容器,即ActivityRecord的容器。

再来说说其中调用的applyAnimations方法部分

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

我们这里openingWcsclosingWcs实际上表示的是应用的容器,即Task;openingAppsclosingApps则是挂在对应应用下面的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。

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

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

判断是否应用动画,传递相关参数

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

/**
     * Applies the app transition animation according the given the layout properties in the
     * window hierarchy.
     *
     * @param lp The layout parameters of the window.
     * @param transit The app transition type indicates what kind of transition to be applied.
     * @param enter Whether the app transition is entering transition or not.
     * @param isVoiceInteraction Whether the container is participating in voice interaction or not.
     * @param sources {@link ActivityRecord}s which causes this app transition animation.
     *
     * @return {@code true} when the container applied the app transition, {@code false} if the
     *         app transition is disabled or skipped.
     *
     * @see #getAnimationAdapter
     */
    boolean applyAnimation(WindowManager.LayoutParams lp, @TransitionOldType int transit,
            boolean enter, boolean isVoiceInteraction,
            @Nullable ArrayList<WindowContainer> sources) {
        //判断是否禁用过渡动画
        if (mWmService.mDisableTransitionAnimation) {
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: transition animation is disabled or skipped. "
                            + "container=%s", this);
            //取消当前动画
            cancelAnimation();
            return false;
        }

        // 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.
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
            //会判断是否有冻结,屏幕是否开启
            if (okToAnimate()) {
                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                        "applyAnimation: transit=%s, enter=%b, wc=%s",
                        AppTransition.appTransitionOldToString(transit), enter, this);
                //传递相关参数,创建AnimationAdapter和AnimationRunnerBuilder,准备启动动画
                applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
            } else {
                //取消当前动画
                cancelAnimation();
            }
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
        //检查指定的窗口容器是否正在进行动画
        return isAnimating();
    }

下面说说里面的几个关键点:

  • 判断是否禁用过渡动画
    mWmService.mDisableTransitionAnimation
    这个变量是在WindowManagerService的构造方法中初始化的

    mDisableTransitionAnimation = context.getResources().getBoolean(
             com.android.internal.R.bool.config_disableTransitionAnimation);
    

    可以发现是读取config_disableTransitionAnimation配置项
    代码路径:frameworks/base/core/res/res/values/symbols.xml

     <java-symbol type="bool" name="config_disableTransitionAnimation" />
    

    定义了这个symbol
    代码路径:frameworks/base/core/res/res/values/config.xml

        <!-- Flag to disable all transition animations -->
        <bool name="config_disableTransitionAnimation">false</bool>
    

    定义了默认值为false,不禁用过渡动画

  • 取消当前动画
    cancelAnimation();

        void cancelAnimation() {
            //处理动画结束时的一些后续操作
            doAnimationFinished(mSurfaceAnimator.getAnimationType(), mSurfaceAnimator.getAnimation());
            //调用SurfaceAnimator.cancelAnimation方法来取消当前正在进行的动画
            mSurfaceAnimator.cancelAnimation();
            //调用unfreeze方法解除对显示的冻结状态,允许显示继续正常更新和渲染
            mSurfaceFreezer.unfreeze(getSyncTransaction());
        }
    

    doAnimationFinished方法在动画播放结束时处理回调逻辑中会调用到,具体见后面【动画移除流程】。

  • 准备动画
    applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
    把前面传递的参数动画的布局、过渡类型、是否可见、是否有语音交互以及需要做动画的ActivityRecord应用的列表,再次传递到applyAnimationUnchecked方法中。
    注意,这里调用的是Task中重写的applyAnimationUnchecked方法,而不是直接调用的WindowContainer中的applyAnimationUnchecked方法。
    因为我们前面是通过前面AppTransitionController.applyAnimations中wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);调用过来的,因此此时的this指针指的是变量wc,即应用对应的Task。
    后面细讲applyAnimationUnchecked方法。

  • 检查动画
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

        final boolean isAnimating(int flags, int typesToCheck) {
            return getAnimatingContainer(flags, typesToCheck) != null;
        }
    

    flags 用于确定要检查的动画类型和范围。
    typesToCheck 用于确定哪些类型的动画需要检查。
    方法内部调用 getAnimatingContainer 方法来获取正在进行动画的窗口容器,并根据返回值判断是否存在符合条件和目标标志的动画。
    如果返回值为 true,则说明存在符合条件的动画;如果返回值为 false,则说明不存在符合条件的动画。

处理最近任务状态的动画

applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
其中参数enter代表的其实就应用的可见性,从前面AppTransitionController.applyAnimations方法中逐步传递过来值有两个

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

启动的应用的可见性为true,桌面的可见性为false
代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java

@Override
    protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
            @TransitionOldType int transit, boolean isVoiceInteraction,
            @Nullable ArrayList<WindowContainer> sources) {
        //获取RecentsAnimationController
        //只有在最近任务中,切换到另一个应用时才会创建
        final RecentsAnimationController control = mWmService.getRecentsAnimationController();
        //RecentsAnimationController不为空
        if (control != null) {
            // We let the transition to be controlled by RecentsAnimation, and callback task's
            // RemoteAnimationTarget for remote runner to animate.
            //应用可见性为true,且当前activity不是桌面或者最近任务
            if (enter && !isActivityTypeHomeOrRecents()) {
                ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                        "applyAnimationUnchecked, control: %s, task: %s, transit: %s",
                        control, asTask(), AppTransition.appTransitionOldToString(transit));
                //执行最近任务动画逻辑
                control.addTaskToTargets(this, (type, anim) -> {
                    for (int i = 0; i < sources.size(); ++i) {
                        sources.get(i).onAnimationFinished(type, anim);
                    }
                });
            }
        //判断是否有返回手势
        } else if (mBackGestureStarted) {
            // Cancel playing transitions if a back navigation animation is in progress.
            // This bit is set by {@link BackNavigationController} when a back gesture is started.
            // It is used as a one-off transition overwrite that is cleared when the back gesture
            // is committed and triggers a transition, or when the gesture is cancelled.
            //返回手势mBackGestureStarted标志位置为false
            mBackGestureStarted = false;
            //设置一个标志为true,表示应跳过应用的过渡动画。
            mDisplayContent.mSkipAppTransitionAnimation = true;
            ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Skipping app transition animation. task=%s", this);
        } else {
            //调用父类WindowContainer的applyAnimationUnchecked方法
            super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
        }
    }

只有在最近任务中,切换到另一个应用时才会创建RecentsAnimationController,因此control的值为空。如果不为空,应用可见性为true,且当前activity不是桌面或者最近任务,则会进入到最近任务的动画处理逻辑。
我们在操作过程中也没有返回手势,因此mBackGestureStarted为false。
所以调用了父类WindowContainer的applyAnimationUnchecked方法。

获取AnimationAdapter并创建动画图层

接着前面super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);进行分析
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
            @TransitionOldType int transit, boolean isVoiceInteraction,
            @Nullable ArrayList<WindowContainer> sources) {
        //获取当前Task
        final Task task = asTask();
        //当前Task不为空,应用变为不可见状态,且应用Task不为桌面或者最近任务
        //可以理解为应用按home键回到桌面的场景
        if (task != null && !enter && !task.isActivityTypeHomeOrRecents()) {
            //对输入法相关insets做处理
            final InsetsControlTarget imeTarget = mDisplayContent.getImeTarget(IME_TARGET_LAYERING);
            final boolean isImeLayeringTarget = imeTarget != null && imeTarget.getWindow() != null
                    && imeTarget.getWindow().getTask() == task;
            // Attach and show the IME screenshot when the task is the IME target and performing
            // task closing transition to the next task.
            if (isImeLayeringTarget && AppTransition.isTaskCloseTransitOld(transit)) {
                mDisplayContent.showImeScreenshot();
            }
        }
        //创建AnimationAdapter
        final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
                transit, enter, isVoiceInteraction);
        //adapters.first指的是创建startBounds为空情况RemoteAnimationAdapterWrapper的对象
        AnimationAdapter adapter = adapters.first;
        //adapters.second指的是创建startBounds不为空情况RemoteAnimationAdapterWrapper的对象
        AnimationAdapter thumbnailAdapter = adapters.second;
        if (adapter != null) {
            if (sources != null) {
                //把需要做的动画的ActivityRecord添加到mSurfaceAnimationSources
                mSurfaceAnimationSources.addAll(sources);
            }

            //创建AnimationRunnerBuilder
            AnimationRunnerBuilder animationRunnerBuilder = new AnimationRunnerBuilder();
             
            //isTaskTransitOld方法中根据transit的值判断返回值,
            //从桌面启动应用时transit为12,表示TRANSIT_OLD_WALLPAPER_CLOSE
            if (isTaskTransitOld(transit)) {
                //设置过渡动画背景色
                animationRunnerBuilder.setTaskBackgroundColor(getTaskAnimationBackgroundColor());
                // TODO: Remove when we migrate to shell (b/202383002)
                //mTaskTransitionSpec不为null
                if (mWmService.mTaskTransitionSpec != null) {
                    //隐藏Insets溢出的部分
                    animationRunnerBuilder.hideInsetSourceViewOverflows(
                            mWmService.mTaskTransitionSpec.animationBoundInsets);
                }
            }

            // Check if the animation requests to show background color for Activity and embedded
            // TaskFragment.
            //获取当前Activity,但我们当前只有Task,因此为空
            final ActivityRecord activityRecord = asActivityRecord();
            //TaskFragment为Task父类,获取到了当前Task
            final TaskFragment taskFragment = asTaskFragment();
            //设置过渡动动画背景色逻辑
            if (adapter.getShowBackground()
                    // Check if it is Activity transition.
                    && ((activityRecord != null && isActivityTransitOld(transit))
                    // Check if it is embedded TaskFragment transition.
                    || (taskFragment != null && taskFragment.isEmbedded()
                    && isTaskFragmentTransitOld(transit)))) {
                final @ColorInt int backgroundColorForTransition;
                if (adapter.getBackgroundColor() != 0) {
                    // If available use the background color provided through getBackgroundColor
                    // which if set originates from a call to overridePendingAppTransition.
                    backgroundColorForTransition = adapter.getBackgroundColor();
                } else {
                    ......
                }
                animationRunnerBuilder.setTaskBackgroundColor(backgroundColorForTransition);
            }

            //进入创建动画图层逻辑
            animationRunnerBuilder.build()
                    .startAnimation(getPendingTransaction(), adapter, !isVisible(),
                            ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);

            //判断是否在动画中显示壁纸
            if (adapter.getShowWallpaper()) {
                //更新pendingLayoutChanges添加标志FINISH_LAYOUT_REDO_WALLPAPER,表示需要重新处理壁纸布局。
                getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
            }
        }
    }

这个方法主要就是获取AnimationAdapter,创建对应的动画图层,我们下面主要讨论这两个点。

创建RemoteAnimationAdapterWrapper实现AnimationAdapter接口

final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
                transit, enter, isVoiceInteraction);

调用的是WindowContainer中的getAnimationAdapter方法,传递了参数动画的布局、过渡类型、是否可见、是否有语音交互。

/**
     * Gets the {@link AnimationAdapter} according the given window layout properties in the window
     * hierarchy.
     *
     * @return The return value will always contain two elements, one for normal animations and the
     * other for thumbnail animation, both can be {@code null}.
     * @See com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord
     * @See LocalAnimationAdapter
     */
    Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
                                                                 @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction) {
        final Pair<AnimationAdapter, AnimationAdapter> resultAdapters;
        //获取当前窗口的裁剪模式
        final int appRootTaskClipMode = getDisplayContent().mAppTransition.getAppRootTaskClipMode();

        // Separate position and size for use in animators.
        //获取屏幕大小,参数appRootTaskClipMode在这个方法中无实际意义
        final Rect screenBounds = getAnimationBounds(appRootTaskClipMode);
        //mTmpRect大小赋值为屏幕大小
        mTmpRect.set(screenBounds);
        //transit值为12,TRANSIT_OLD_WALLPAPER_CLOSE,不进入该流程
        if (this.asTask() != null && isTaskTransitOld(transit)) {
            this.asTask().adjustAnimationBoundsForTransition(mTmpRect);
        }
        //设置动画位置为左上角顶点(0, 0)
        getAnimationPosition(mTmpPoint);
        mTmpRect.offsetTo(0, 0);

        final AppTransition appTransition = getDisplayContent().mAppTransition;
        //创建RemoteAnimationController
        final RemoteAnimationController controller = appTransition.getRemoteAnimationController();
        //AppTransition.isChangeTransitOld(transit),transit值为12,返回false
        //enter 应用可见性,启动应用的可见性为true,桌面可见性为false
        //isChangingAppTransition(),判断当前应用是否切换过渡,
        //其取决于参数DisplayContent.mChangingContainers中是否有值
        /** 
        有三种情况会给DisplayContent.mChangingContainers中添加值
        1.{@link Task}在全屏和浮窗之间发生切换
        2.{@link TaskFragment}已组织好并且正在更改窗口边界
        3.{@link ActivityRecord}被重新分配到一个有组织的{@link TaskFragment}中
        **/
        //这里我们isChangingAppTransition()值为false,即isChanging值为false
        final boolean isChanging = AppTransition.isChangeTransitOld(transit) && enter
                && isChangingAppTransition();

        // Delaying animation start isn't compatible with remote animations at all.
        //mSurfaceAnimator.isAnimationStartDelayed()判断动画是否延迟开启,
        //false表示不延迟,true表示延迟,我们这里值为false
        if (controller != null && !mSurfaceAnimator.isAnimationStartDelayed()) {
            // Here we load App XML in order to read com.android.R.styleable#Animation_showBackdrop.
            boolean showBackdrop = false;
            // Optionally set backdrop color if App explicitly provides it through
            // {@link Activity#overridePendingTransition(int, int, int)}.
            @ColorInt int backdropColor = 0;
            //isFromActivityEmbedding(),返回的是RemoteAnimationController中的变量mIsActivityEmbedding
            //我们这里前面通过getRemoteAnimationController()创建RemoteAnimationController会对其赋值为false
            //因此下面流程不进行
            if (controller.isFromActivityEmbedding()) {
                ......
            }
            //创建一个和mTmpRect相同大小和位置的矩阵
            final Rect localBounds = new Rect(mTmpRect);
            localBounds.offsetTo(mTmpPoint.x, mTmpPoint.y);
            final RemoteAnimationController.RemoteAnimationRecord adapters;
            //前面isChanging的值为false,因此!isChanging为true
            //enter 应用可见性,启动应用的可见性为true,桌面可见性为false
            //这里主要判断的是isClosingWhenResizing()方法的值,
            //这个值同样取决于参数DisplayContent.mChangingContainers中是否有值
            //这里我们isClosingWhenResizing()值为false
            if (!isChanging && !enter && isClosingWhenResizing()) {
                // Container that is closing while resizing. Pass in the closing start bounds, so
                // the animation can start with the correct bounds, there won't be a snapshot.
                // Cleanup the mClosingChangingContainers so that when the animation is finished, it
                // will reset the surface.
                //调整大小时正在关闭的容器。传入关闭的开始边界,这样动画就可以从正确的边界开始,不会有快照。
                //清理mClosingChangingContainers,以便在动画完成时重置surface。
                final Rect closingStartBounds = getDisplayContent().mClosingChangingContainers
                        .remove(this);
                adapters = controller.createRemoteAnimationRecord(
                        this, mTmpPoint, localBounds, screenBounds, closingStartBounds,
                        showBackdrop, false /* shouldCreateSnapshot */);
            } else {
                //isChanging为false,所以startBounds为null
                final Rect startBounds = isChanging ? mSurfaceFreezer.mFreezeBounds : null;
                //创建RemoteAnimationRecord,之后创建RemoteAnimationAdapterWrapper
                //RemoteAnimationAdapterWrapper实现了AnimationAdapter接口
                adapters = controller.createRemoteAnimationRecord(
                        this, mTmpPoint, localBounds, screenBounds, startBounds, showBackdrop);
            }
            if (backdropColor != 0) {
                adapters.setBackDropColor(backdropColor);
            }
            if (!isChanging) {
                //根据enter的值设置mMode的值
                adapters.setMode(enter
                        ? RemoteAnimationTarget.MODE_OPENING
                        : RemoteAnimationTarget.MODE_CLOSING);
            }
            //RemoteAnimationAdapterWrapper会根据startBounds变量是否为空进行不同的创建方式
            //startBounds变量为空的情况,保存到Pair类的first变量中
            //startBounds变量不为空的情况,保存到Pair类的second变量中
            resultAdapters = new Pair<>(adapters.mAdapter, adapters.mThumbnailAdapter);
        } else if (isChanging) {
            //创建的是LocalAnimationAdapter,非远程动画流程
            ......
        } else {
            //创建的是LocalAnimationAdapter,非远程动画流程
            ......
        }
        return resultAdapters;
    }

通过创建RemoteAnimationRecord来创建RemoteAnimationAdapterWrapper

adapters = controller.createRemoteAnimationRecord(
                        this, mTmpPoint, localBounds, screenBounds, startBounds, showBackdrop);

根据前面设置的参数我们可以知道传递的值分别为:
this指的是应用Task,以及桌面Task;
startBounds为 null ;mTmpPoint为(0,0) ;localBounds为屏幕分辨率的矩形; showBackdrop为false。

代码路径:frameworks/base/services/core/java/com/android/server/wm/RemoteAnimationController.java

    /**
     * Creates an animation record for each individual {@link WindowContainer}.
     *
     * @param windowContainer The windows to animate.
     * @param position        The position app bounds relative to its parent.
     * @param localBounds     The bounds of the app relative to its parent.
     * @param endBounds       The end bounds after the transition, in screen coordinates.
     * @param startBounds     The start bounds before the transition, in screen coordinates.
     * @param showBackdrop    To show background behind a window during animation.
     * @return The record representing animation(s) to run on the app.
     */
    RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
            Point position, Rect localBounds, Rect endBounds, Rect startBounds,
            boolean showBackdrop) {
        return createRemoteAnimationRecord(windowContainer, position, localBounds, endBounds,
                startBounds, showBackdrop, startBounds != null /* shouldCreateSnapshot */);
    }
    
    /**
     * Creates an animation record for each individual {@link WindowContainer}.
     *
     * @param windowContainer The windows to animate.
     * @param position        The position app bounds relative to its parent.
     * @param localBounds     The bounds of the app relative to its parent.
     * @param endBounds       The end bounds after the transition, in screen coordinates.
     * @param startBounds     The start bounds before the transition, in screen coordinates.
     * @param showBackdrop    To show background behind a window during animation.
     * @param shouldCreateSnapshot   Whether this target should create a snapshot animation.
     * @return The record representing animation(s) to run on the app.
     */
    RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
            Point position, Rect localBounds, Rect endBounds, Rect startBounds,
            boolean showBackdrop, boolean shouldCreateSnapshot) {
        ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
                windowContainer);
        final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
                localBounds, endBounds, startBounds, showBackdrop, shouldCreateSnapshot);
        mPendingAnimations.add(adapters);
        return adapters;
    }

传递参数,创建RemoteAnimationRecord,其中shouldCreateSnapshot参数的值取决于startBounds是否为空,我们这里startBounds为空,所以shouldCreateSnapshot为false。

/**
     * Contains information about a remote-animation for one WindowContainer. This keeps track of,
     * potentially, multiple animating surfaces (AdapterWrappers) associated with one
     * Window/Transition. For example, a change transition has an adapter controller for the
     * main window and an adapter controlling the start-state snapshot.
     * <p>
     * This can be thought of as a bridge between the information that the remote animator sees (via
     * {@link RemoteAnimationTarget}) and what the server sees (the
     * {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces).
     */
    public class RemoteAnimationRecord {
        ......
        RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
                Rect endBounds, @Nullable Rect startBounds, boolean showBackdrop,
                boolean shouldCreateSnapshot) {
            mWindowContainer = windowContainer;
            mShowBackdrop = showBackdrop;
            if (startBounds != null) {
                ......
            } else {
                mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
                        new Rect(), mShowBackdrop);
                mStartBounds = null;
            }
        }
        ......
    }

RemoteAnimationAdapterWrapper会根据startBounds变量是否为空进行不同的创建方式,我们这里主要关注其为空的流程。
startBounds为空,走else流程

 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
                        new Rect(), mShowBackdrop);

这里通过new Rect()空矩阵(上下左右四点均为0),创建了一个startBounds

    class RemoteAnimationAdapterWrapper implements AnimationAdapter {
        ......

        RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
                Rect localBounds, Rect endBounds, Rect startBounds, boolean showBackdrop) {
            mRecord = record;
            mPosition.set(position.x, position.y);
            mLocalBounds = localBounds;
            mEndBounds.set(endBounds);
            mStartBounds.set(startBounds);
            mShowBackdrop = showBackdrop;
        }
        ......

把传递的参数赋值给了RemoteAnimationAdapterWrapper的成员变量。
这里我们的RemoteAnimationAdapterWrapper实现了AnimationAdapter接口。

通过startAnimation方法创建动画图层

            animationRunnerBuilder.build()
                    .startAnimation(getPendingTransaction(), adapter, !isVisible(),
                            ANIMATION_TYPE_APP_TRANSITION, thumbnailAdapter);

getPendingTransaction():指的是一个事务。
adapter:是前面赋值AnimationAdapter adapter = adapters.first;,即创建的startBounds变量为空情况的RemoteAnimationAdapterWrapper对象。
!isVisible():获取当前可见性后取反。应用当前可见性为false,即传递true;桌面当前可见性为true,即传递false。
ANIMATION_TYPE_APP_TRANSITION:过渡动画类型。
thumbnailAdapter:是前面赋值AnimationAdapter thumbnailAdapter = adapters.second;,即创建的startBounds变量不为空情况的RemoteAnimationAdapterWrapper对象。

animationRunnerBuilder是AnimationRunnerBuilder对象,调用了其build方法中的startAnimation方法。我们这里调用的startAnimation方法是一个函数式接口。

    private interface IAnimationStarter {
        void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
                            @AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
    }

startAnimation方法的实现在AnimationRunnerBuilder中的build方法中return的 (Transaction t, AnimationAdapter adapter, boolean hidden, AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> {......}

    private class AnimationRunnerBuilder {
        /**
         * Runs when the surface stops animating
         */
        private final List<Runnable> mOnAnimationFinished = new LinkedList<>();
        /**
         * Runs when the animation is cancelled but the surface is still animating
         */
        private final List<Runnable> mOnAnimationCancelled = new LinkedList<>();
        ......
        private IAnimationStarter build() {
            return (Transaction t, AnimationAdapter adapter, boolean hidden,
                    @AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> {
                startAnimation(getPendingTransaction(), adapter, !isVisible(), type,
                        (animType, anim) -> mOnAnimationFinished.forEach(Runnable::run),
                        () -> mOnAnimationCancelled.forEach(Runnable::run), snapshotAnim);
            };
        }
    }

除了传递之前的参数之外,在return的(Transaction t, AnimationAdapter adapter, boolean hidden, AnimationType int type, @Nullable AnimationAdapter snapshotAnim) -> {......}中调用了startAnimation方法,传递了回调函数(animType, anim) -> mOnAnimationFinished.forEach(Runnable::run)() -> mOnAnimationCancelled.forEach(Runnable::run),会执行mOnAnimationFinished和mOnAnimationCancelled这两个变量中的Runnable。

继续查看startAnimation方法

    /**
     * Starts an animation on the container.
     *
     * @param anim                       The animation to run.
     * @param hidden                     Whether our container is currently hidden. TODO This should use isVisible at
     *                                   some point but the meaning is too weird to work for all containers.
     * @param type                       The type of animation defined as {@link AnimationType}.
     * @param animationFinishedCallback  The callback being triggered when the animation finishes.
     * @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
     *                                   cancel call to the underlying AnimationAdapter.
     * @param snapshotAnim               The animation to run for the snapshot. {@code null} if there is no
     *                                   snapshot.
     */
    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
                        @AnimationType int type,
                        @Nullable OnAnimationFinishedCallback animationFinishedCallback,
                        @Nullable Runnable animationCancelledCallback,
                        @Nullable AnimationAdapter snapshotAnim) {
        ProtoLog.v(WM_DEBUG_ANIM, "Starting animation on %s: type=%d, anim=%s",
                this, type, anim);

        // TODO: This should use isVisible() but because isVisible has a really weird meaning at
        // the moment this doesn't work for all animatable window containers.
        mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback,
                animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
    }
  • 入参含义
    t:这是一个对象,用于描述一系列的窗口操作,例如移动、调整大小、绘制等。这些操作在WMS中排队,并在适当的时机应用到窗口上。
    anim:这是对动画进行封装的类。它包含了一些关于如何开始、更新和结束动画的信息。传递的也就是前面WindowContainer.getAnimationAdapter中创建startBounds变量为空情况的RemoteAnimationAdapterWrapper对象。
    hidden:这个布尔值表示窗口是否隐藏。如果窗口是隐藏的,那么就不会显示动画。前面传递的!isVisible()值为false。
    type:这个整数代表了动画的类型。这里我们传递的是ANIMATION_TYPE_APP_TRANSITION,即图层上显示的app_transition。
    animationFinishedCallbackanimationCancelledCallback:这两个是回调函数,分别在动画完成和动画取消时被调用。
    snapshotAnim:这个参数是给定动画的快照。如果参数为null,那么就表示没有快照。传递过来的是变量thumbnailAdapter,即startBounds变量不为空情况的RemoteAnimationAdapterWrapper对象。

  • 代码含义
    关键的代码只有这一句mSurfaceAnimator.startAnimation(t, anim, hidden, type, animationFinishedCallback, animationCancelledCallback, snapshotAnim, mSurfaceFreezer);
    这行代码调用了SurfaceAnimator的startAnimation方法来启动动画。SurfaceAnimator的作用主要是控制窗口动画,它是窗口动画的中控,通过操控mLeash对象来实现窗口的大小、位置、透明度等动画属性的改变。这个方法需要一系列参数,包括上面解释的所有参数,还有一个SurfaceFreezer对象mSurfaceFreezer,它可以在动画开始时冻结窗口的更新,以防止在动画过程中窗口的内容闪烁。
    mSurfaceAnimator和mSurfaceFreezer是在WindowContainer的构造方法中初始化的

    class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
        InsetsControlTarget {
        ......
    	WindowContainer(WindowManagerService wms) {
    	     mWmService = wms;
    	     mTransitionController = mWmService.mAtmService.getTransitionController();
    	     mPendingTransaction = wms.mTransactionFactory.get();
    	     mSyncTransaction = wms.mTransactionFactory.get();
    	     mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
    	     mSurfaceFreezer = new SurfaceFreezer(this, wms);
    	 }
    	 ......
    }
    

通过SurfaceAnimator中创建leash

代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java

    /**
     * Starts an animation.
     *
     * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the
     *             component responsible for running the animation. It runs the animation with
     *             {@link AnimationAdapter#startAnimation} once the hierarchy with
     *             the Leash has been set up.
     * @param hidden Whether the container holding the child surfaces is currently visible or not.
     *               This is important as it will start with the leash hidden or visible before
     *               handing it to the component that is responsible to run the animation.
     * @param animationFinishedCallback The callback being triggered when the animation finishes.
     * @param animationCancelledCallback The callback is triggered after the SurfaceAnimator sends a
     *                                   cancel call to the underlying AnimationAdapter.
     * @param snapshotAnim The animation to run for the snapshot. {@code null} if there is no
     *                     snapshot.
     */
    void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden,
            @AnimationType int type,
            @Nullable OnAnimationFinishedCallback animationFinishedCallback,
            @Nullable Runnable animationCancelledCallback,
            @Nullable AnimationAdapter snapshotAnim, @Nullable SurfaceFreezer freezer) {
        //开始新的动画之前,取消之前的动画
        //参数含义:t(是一个事务对象),true(表示动画正在重新启动),和true(表示向前取消)
        cancelAnimation(t, true /* restarting */, true /* forwardCancel */);
        //初始化参数,把WindowContainer.startAnimation中传递的参数赋值给对应变量
        mAnimation = anim;
        mAnimationType = type;
        mSurfaceAnimationFinishedCallback = animationFinishedCallback;
        mAnimationCancelledCallback = animationCancelledCallback;
        //获取当前窗口的SurfaceControl
        final SurfaceControl surface = mAnimatable.getSurfaceControl();
        //没有surface,则取消当前的动画
        if (surface == null) {
            Slog.w(TAG, "Unable to start animation, surface is null or no children.");
            cancelAnimation();
            return;
        }
        //调用SurfaceFreezer中takeLeashForAnimation()获取mLeash,但是SurfaceFreezer中没有被初始化,所以这里的mLeash还是为null
        mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
        if (mLeash == null) {
        	//创建mLeash
            mLeash = createAnimationLeash(mAnimatable, surface, t, type,
                    mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
                    0 /* y */, hidden, mService.mTransactionFactory);
            //创建动画“leash”后执行的一些操作,包括重置图层、重新分配图层以及重置Surface的位置
            mAnimatable.onAnimationLeashCreated(t, mLeash);
        }
        //处理动画开始时进行一些设置和准备工作
        mAnimatable.onLeashAnimationStarting(t, mLeash);
        if (mAnimationStartDelayed) {
            ProtoLog.i(WM_DEBUG_ANIM, "Animation start delayed for %s", mAnimatable);
            return;
        }
        //将leash传给AnimationAdapter
        mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
        if (ProtoLogImpl.isEnabled(WM_DEBUG_ANIM)) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            mAnimation.dump(pw, "");
            ProtoLog.d(WM_DEBUG_ANIM, "Animation start for %s, anim=%s", mAnimatable, sw);
        }
        //获取一个快照,并使用该快照来执行动画,我们这里snapshotAnim为null,因此不涉及
        if (snapshotAnim != null) {
            mSnapshot = freezer.takeSnapshotForAnimation();
            if (mSnapshot == null) {
                Slog.e(TAG, "No snapshot target to start animation on for " + mAnimatable);
                return;
            }
            mSnapshot.startAnimation(t, snapshotAnim, type);
        }
    }

入参就是前面WindowContainer.startAnimation中传递的参数。

获取当前窗口的surface

final SurfaceControl surface = mAnimatable.getSurfaceControl();
mAnimatable是Animatable接口的对象,WindowContainer实现了Animatable接口。
在WindowContainer构造方法中初始化mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
而SurfaceAnimator的构造方法是

    SurfaceAnimator(Animatable animatable,
            @Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback,
            WindowManagerService service) {
        mAnimatable = animatable;
        mService = service;
        mStaticAnimationFinishedCallback = staticAnimationFinishedCallback;
        mInnerAnimationFinishedCallback = getFinishedCallback(staticAnimationFinishedCallback);
    }

也就是说实际上是把this赋值给了mAnimatable,因此mAnimatable就代表了当前的窗口。

从前面的代码可以看出远程动画涉及到打开的应用和关闭的应用,这两个应用的动画。

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

即我们这里第一次获取当前窗口mAnimatable时,代表的是正在打开的应用,即应用Task;第二次获取的则是桌面,即桌面Task。

获取Leash

mLeash = freezer != null ? freezer.takeLeashForAnimation() : null;
根据freezer是否为null来确定mLeash的值,我们这里freezer是从WindowContainer.startAnimation方法中传递过来的mSurfaceFreezer,这个变量在WindowContainer的构造方法中初始化mSurfaceFreezer = new SurfaceFreezer(this, wms);,因此mSurfaceFreezer不为null,即freezer不为nullfreezer != null true,所以走freezer.takeLeashForAnimation()
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceFreezer.java

	SurfaceControl mLeash;
    /**
     * Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation.
     * By transferring the leash, this will no longer try to clean-up the leash when finished.
     */
    SurfaceControl takeLeashForAnimation() {
        SurfaceControl out = mLeash;
        mLeash = null;
        return out;
    }

mLeash在SurfaceFreezer类中并没有初始化,因此我们的mLeash值为null,所以out的值同样为null,最终SurfaceAnimator类中的mLeash获取到的值为null

创建Leash

mLeash为null,使用createAnimationLeash方法创建Leash

            mLeash = createAnimationLeash(mAnimatable, surface, t, type,
                    mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), 0 /* x */,
                    0 /* y */, hidden, mService.mTransactionFactory);

入参含义
mAnimatable:当前窗口。
surface:当前窗口的surface。
t:一个事务对象,用于执行一系列操作。
type:动画类型。
mAnimatable.getSurfaceWidth()mAnimatable.getSurfaceHeight():窗口surface尺寸的参数。
0 /* x */0 /* y */:坐标位置
hidden:一个布尔值,表示是否隐藏。
mService.mTransactionFactory:一个事务工厂对象,用于创建新的事务。

SurfaceAnimator.createAnimationLeash()
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java

    static SurfaceControl createAnimationLeash(Animatable animatable, SurfaceControl surface,
            Transaction t, @AnimationType int type, int width, int height, int x, int y,
            boolean hidden, Supplier<Transaction> transactionFactory) {
        /* log add start*/
        Slog.i("WindowManager:","createAnimationLeash type = " + animationTypeToString(type) , new Exception());
        /* log add end*/
        ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to leash for %s", animatable);
        //通过SurfaceControl.Builder创建leash
        final SurfaceControl.Builder builder = animatable.makeAnimationLeash()
                .setParent(animatable.getAnimationLeashParent())
                .setName(surface + " - animation-leash of " + animationTypeToString(type))
                // TODO(b/151665759) Defer reparent calls
                // We want the leash to be visible immediately because the transaction which shows
                // the leash may be deferred but the reparent will not. This will cause the leashed
                // surface to be invisible until the deferred transaction is applied. If this
                // doesn't work, you will can see the 2/3 button nav bar flicker during seamless
                // rotation.
                .setHidden(hidden)
                .setEffectLayer()
                .setCallsite("SurfaceAnimator.createAnimationLeash");
        //通过前面的SurfaceControl.Builder创建leash
        final SurfaceControl leash = builder.build();
        //其他属性设置
        t.setWindowCrop(leash, width, height);
        t.setPosition(leash, x, y);
        t.show(leash);
        t.setAlpha(leash, hidden ? 0 : 1);
        //当前窗口的surface重新绑定到新创建的leash上
        t.reparent(surface, leash);
        return leash;
    }

传递当前窗口animatable,为其和其父节点之间添加surface。第一次创建的是正在打开的应用的动画,第二次是桌面关闭时的动画。

下面我们解读一下,leash是如何创建并加入其中的

通过SurfaceControl.Builder创建leash
  • animatable.makeAnimationLeash()
    代码路径: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

        public Builder makeAnimationLeash() {
            return makeSurface().setContainerLayer();
        }
    

    创建一个图层作为容器layer

  • setParent(animatable.getAnimationLeashParent())
    这段代码我们分成两个部分来看,即setParent()getAnimationLeashParent()
    1.setParent()
    代码路径:frameworks/base/core/java/android/view/SurfaceControl.java

            /**
             * Set a parent surface for our new SurfaceControl.
             *
             * Child surfaces are constrained to the onscreen region of their parent.
             * Furthermore they stack relatively in Z order, and inherit the transformation
             * of the parent.
             *
             * @param parent The parent control.
             */
            @NonNull
            public Builder setParent(@Nullable SurfaceControl parent) {
                mParent = parent;
                return this;
            }
    

    这个段代码很简单,就是给当前SurfaceControl设置一个父SurfaceControl。
    2.getAnimationLeashParent()
    代码路径: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

        @Override
        public SurfaceControl getAnimationLeashParent() {
            return getParentSurfaceControl();
        }
        
        /*
         * @return The SurfaceControl parent for this containers SurfaceControl.
         *         The SurfaceControl must be valid if non-null.
         */
        @Override
        public SurfaceControl getParentSurfaceControl() {
            final WindowContainer parent = getParent();
            if (parent == null) {
                return null;
            }
            return parent.getSurfaceControl();
        }
    
        /**
         * @return The SurfaceControl for this container.
         * The SurfaceControl must be valid if non-null.
         */
        @Override
        public SurfaceControl getSurfaceControl() {
            return mSurfaceControl;
        }
    

    简单来说,就是获取当前窗口父SurfaceControl。

    那么合起来setParent(animatable.getAnimationLeashParent())的意思就是,把当前新创建的SurfaceControl(leash)的父亲设置为当前窗口父亲的SurfaceControl
    即此时leash图层和当前窗口应用Task的父亲均是DefaultTaskDsiplayArea,两人还在当兄弟。
    简图如下:
    在这里插入图片描述
    此时创建的是应用Task的动画,桌面Task的动画尚未创建,待应用Task动画创建完成后,才会去走正在关闭的应用(桌面)的动画逻辑。
    桌面Task是一直挂在DefaultTaskDsiplayArea上的,这里我们先不关注桌面Task节点。

  • setEffectLayer()
    代码路径:frameworks/base/core/java/android/view/SurfaceControl.java

            /**
             * Indicate whether an 'EffectLayer' is to be constructed.
             *
             * An effect layer behaves like a container layer by default but it can support
             * color fill, shadows and/or blur. These layers will not have an associated buffer.
             * When created, this layer has no effects set and will be transparent but the caller
             * can render an effect by calling:
             *  - {@link Transaction#setColor(SurfaceControl, float[])}
             *  - {@link Transaction#setBackgroundBlurRadius(SurfaceControl, int)}
             *  - {@link Transaction#setShadowRadius(SurfaceControl, float)}
             *
             * @hide
             */
            public Builder setEffectLayer() {
                mFlags |= NO_COLOR_FILL;
                //清空缓冲区设置
                unsetBufferSize();
                return setFlags(FX_SURFACE_EFFECT, FX_SURFACE_MASK);
            }
    

    设置为EffectLayer。它是一种特殊类型的SurfaceControl层,它默认表现得像一个容器层,但可以支持颜色填充、阴影和/或模糊效果。
    这个EffectLayer主要就是用于实现一些视觉效果。
    默认的注释里面也说明可以使用这些方法来渲染一个效果:
    Transaction#setColor(SurfaceControl, float[]):使用给定的颜色数组设置该层的颜色。
    Transaction#setBackgroundBlurRadius(SurfaceControl, int):设置背景模糊的半径。
    Transaction#setShadowRadius(SurfaceControl, float):设置阴影的半径。

final SurfaceControl leash = builder.build();
最后通过build()方法创建leash

当前窗口的surface重新绑定到新创建的leash上

t.reparent(surface, leash);
这里的surface指的就是从前面传递的当前窗口的SurfaceControl。
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java

        /**
         * Re-parents a given layer to a new parent. Children inherit transform (position, scaling)
         * crop, visibility, and Z-ordering from their parents, as if the children were pixels within the
         * parent Surface.
         *
         * @param sc The SurfaceControl to reparent
         * @param newParent The new parent for the given control.
         * @return This Transaction
         */
        @NonNull
        public Transaction reparent(@NonNull SurfaceControl sc,
                @Nullable SurfaceControl newParent) {
            //检查传入的SurfaceControl对象是否满足某些预设条件
            checkPreconditions(sc);
            long otherObject = 0;
            if (newParent != null) {
            	//检查新父对象是否被释放。如果已经被释放,那么它会抛出异常。
                newParent.checkNotReleased();
                //新父对象不为null且未被释放,那么将新父对象的Native对象赋值给otherObject。
                otherObject = newParent.mNativeObject;
            }
            //传入了三个参数:1.当前对象的Native对象 2.被重新设置父对象的SurfaceControl的Native对象 3.新父对象的Native对象。
            //用于实现重新设置父对象的具体操作。
            nativeReparent(mNativeObject, sc.mNativeObject, otherObject);
            //把被重新设置父对象的SurfaceControl和新父对象存储到mReparentedSurfaces这个map中。
            mReparentedSurfaces.put(sc, newParent);
            return this;
        }

@NonNull SurfaceControl sc: 表示要被重新设置父对象的SurfaceControl对象。这个参数不能为null。
@Nullable SurfaceControl newParent: 表示新的父SurfaceControl对象。可以为null,表示没有新的父对象。
这个方法主要就是把当前窗口的SurfaceControl的父亲,修改为leash
mReparentedSurfaces是ArrayMap对象,以键值对的形式临时存储父子关系,key值存储SurfaceControl对象,value为其父SurfaceControl对象。

即此时leash图层变成了当前窗口应用Task图层的父亲,如简易图所示:
曾经我们是兄弟,如今我是你爸爸~
在这里插入图片描述

需要注意的是,Task和DefaultTaskDsiplayArea容器之前的关系并未改变,创建leash图层的过程只是改变的是surface之间的关系
即leash图层是DefaultTaskDsiplayArea图层的父亲,Task的图层是leash图层的父亲,但是Task的父亲仍然是DefaultTaskDsiplayArea。
实际关系情况如下图所示:
在这里插入图片描述

所以DefaultTaskDsiplayArea和leash实际上是表面父子,leash和Task也是表面父子

leash的surface调整

mAnimatable.onAnimationLeashCreated(t, mLeash);
入参是一个事务(用于操作窗口系统的底层API)和一个SurfaceControl对象(表示一个可以控制和操作Surface的接口)
把创建好的mLeash传递到onAnimationLeashCreated方法中,做一些Surface调整操作。
该方法实现在WindowContainer中。
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    void reassignLayer(Transaction t) {
        final WindowContainer parent = getParent();
        if (parent != null) {
            parent.assignChildLayers(t);
        }
    }
    
    void resetSurfacePositionForAnimationLeash(Transaction t) {
        t.setPosition(mSurfaceControl, 0, 0);
        final SurfaceControl.Transaction syncTransaction = getSyncTransaction();
        if (t != syncTransaction) {
            // Avoid restoring to old position if the sync transaction is applied later.
            syncTransaction.setPosition(mSurfaceControl, 0, 0);
        }
        mLastSurfacePosition.set(0, 0);
    }

    @Override
    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
        mLastLayer = -1;
        mAnimationLeash = leash;
        reassignLayer(t);

        // Leash is now responsible for position, so set our position to 0.
        resetSurfacePositionForAnimationLeash(t);
    }

这段代码主要用于在创建新的leash时,重置动画目标的位置,并初始化一些动画相关的状态。同时,可能还用于重新分配或者设置子容器的图层。
首先,在新的动画(leash)被创建时被调用。在这个方法中,首先将mLastLayer(可能表示上一个图层或者上一个动画目标)设置为-1,然后保存传入的leashmAnimationLeash(后面removeLeash流程中会用到mAnimationLeash)。
之后,调用reassignLayer(t)方法,这个方法获取这个视图的父容器,如果父容器存在,那么就调用父容器的assignChildLayers(t)方法(用于调整其所有child的z-order)。
最后,为了确保leash现在位置的控制,调用resetSurfacePositionForAnimationLeash(t)方法将Surface的位置重置为(0,0),重置界面元素的位置以便进行动画。
注:Z-order也被称为深度顺序(depth order)或Z轴顺序,它用于确定图层(Layers)在屏幕上的堆叠顺序。简单来说,Z-order就是图层在Z轴上的位置,Z轴位置越低,图层越在底层,Z轴位置越高,图层越在顶层。

处理动画开始时进行一些设置和准备工作

mAnimatable.onLeashAnimationStarting(t, mLeash);
入参同样是一个事务(用于操作窗口系统的底层API)和一个SurfaceControl对象(表示一个可以控制和操作Surface的接口)
onLeashAnimationStarting方法是在ActivityRecord中实现的。
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

    @Override
    public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) {
        if (mAnimatingActivityRegistry != null) {
        	//1.将正在启动或者有动画效果的Activity添加到列表中,以便于管理和控制这些Activity的动画效果。
            mAnimatingActivityRegistry.notifyStarting(this);
        }

        // If the animation needs to be cropped then an animation bounds layer is created as a
        // child of the root pinned task or animation layer. The leash is then reparented to this
        // new layer.
        //2.否需要创建一个动画边界层
        if (mNeedsAnimationBoundsLayer) {
        	//设置临时矩形为空
            mTmpRect.setEmpty();
            //调用方法检查当前的活动转移是否在任务内部。
            //如果是,则获取任务的边界到临时矩形mTmpRect。如果不是,则获取RootTask的边界。
            if (getDisplayContent().mAppTransitionController.isTransitWithinTask(
                    getTransit(), task)) {
                task.getBounds(mTmpRect);
            } else {
                final Task rootTask = getRootTask();
                if (rootTask == null) {
                    return;
                }
                // Set clip rect to root task bounds.
                rootTask.getBounds(mTmpRect);
            }
            //创建动画边界层
            mAnimationBoundsLayer = createAnimationBoundsLayer(t);

            // Crop to root task bounds.
            //设置leash的层为0
            //leash将被放置在Z轴的最底层,如果有其他层级的SurfaceControl对象,它们将会覆盖在leash之上。
            t.setLayer(leash, 0);
            //并设置AnimationBoundsLayer的层为上一个层的值,保证leash在AnimationBoundsLayer下面
            t.setLayer(mAnimationBoundsLayer, getLastLayer());

            // Reparent leash to animation bounds layer.
            //重新将leash的父节点设置为动画边界层。
            t.reparent(leash, mAnimationBoundsLayer);
        }
    }

    private SurfaceControl createAnimationBoundsLayer(Transaction t) {
        ProtoLog.i(WM_DEBUG_APP_TRANSITIONS_ANIM, "Creating animation bounds layer");
        final SurfaceControl.Builder builder = makeAnimationLeash()
        		//给AnimationBoundsLayer设置父节点为Leash的父节点
        		//即把动画边界层的父节点设置为windowToken
                .setParent(getAnimationLeashParent())
                .setName(getSurfaceControl() + " - animation-bounds")
                .setCallsite("ActivityRecord.createAnimationBoundsLayer");
        final SurfaceControl boundsLayer = builder.build();
        t.show(boundsLayer);
        return boundsLayer;
    }

这个方法其实主要就是做了两件事:
1.根据mAnimatingActivityRegistry的值判断,是否需要把有动画效果的Activity添加到列表中
2.根据mNeedsAnimationBoundsLayer的值判断,否需要创建一个动画边界层

createAnimationBoundsLayer就是创建了一个SurfaceControl。
getLastLayer()用于返回当前窗口的最高(或最后)层级。假设我们有一个窗口管理系统中,窗口的层级从0开始编号。当一个新窗口创建时,它可能被赋予层级0。然后,如果这个新窗口被另一个窗口覆盖,那么新窗口的层级可能会更新为1,依此类推。
通过使用AnimationBoundsLayer,可以定义一个矩形区域,该区域可以作为动画的边界。当动画开始时,它只在该定义的区域内显示,不会超出这个边界。AnimationBoundsLayer的主要作用是限制动画的显示区域,以确保动画不会影响到应用程序的其他部分。

将leash传给RemoteAnimationAdapterWrapper,执行动画

mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);
mAnimation是AnimationAdapter接口的对象,调用其startAnimation方法,传递mLeash(动画)、t(事务)、type(动画类型)和mInnerAnimationFinishedCallback(回调函数)。
mAnimation的值是前面WindowContainer.startAnimation传递的anim,这个anim实际上就是传递的也就是前面WindowContainer.getAnimationAdapter中创建startBounds变量为空情况的RemoteAnimationAdapterWrapper对象。因此这里调用接口AnimationAdapter的方法startAnimation正是在RemoteAnimationAdapterWrapper中实现的。
代码路径:frameworks/base/services/core/java/com/android/server/wm/RemoteAnimationController.java

        @Override
        public void startAnimation(SurfaceControl animationLeash, Transaction t,
                @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
            ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");

            //设置动画的起始位置和窗口裁剪区域
            if (mStartBounds.isEmpty()) {
                // Restore position and stack crop until client has a chance to modify it.
                t.setPosition(animationLeash, mPosition.x, mPosition.y);
                t.setWindowCrop(animationLeash, mEndBounds.width(), mEndBounds.height());
            } else {
                // Offset the change animation leash to the relative start position in parent.
                // (mPosition) is the relative end position in parent container.
                // (mStartBounds - mEndBounds) is the position difference between start and end.
                // (mPosition + mStartBounds - mEndBounds) will be the relative start position.
                t.setPosition(animationLeash, mPosition.x + mStartBounds.left - mEndBounds.left,
                        mPosition.y + mStartBounds.top - mEndBounds.top);
                t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height());
            }
            //保存动画图层到mCapturedLeash
            mCapturedLeash = animationLeash;
            //当动画完成时的回调函数 finishCallback保存到mCapturedFinishCallback。
            mCapturedFinishCallback = finishCallback;
            //保存动画类型
            mAnimationType = type;
        }

这个方法的主要目的是根据提供的起始和结束边界来设置动画的起始位置和窗口裁剪区域,并保存相关信息到RemoteAnimationAdapterWrapper的成员变量中,以供后续使用。

启动并显示动画流程

layoutRedo = appTransition.goodToGo(transit, topOpeningApp);
在最开始的AppTransitionController.handleAppTransitionReady方法中调用AppTransition.goodToGo方法开始启动并显示动画的流程。
入参同样都是在handleAppTransitionReady方法中初始化的:
参数transit的值通过getTransitCompatType方法中获取,是TRANSIT_OLD_WALLPAPER_CLOSE(12);
参数topOpeningApp指的是正在打开的应用列表 (mOpeningApps) 中的顶层应用,即应用Task对应的ActivityRecord,也就是我们在桌面启动的应用的ActivityRecord。

判断是否需要重新进行布局,RemoteAnimationController状态

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

/**
     * @return bit-map of WindowManagerPolicy#FINISH_LAYOUT_REDO_* to indicate whether another
     *         layout pass needs to be done
     */
    int goodToGo(@TransitionOldType int transit, ActivityRecord topOpeningApp) {
        mNextAppTransitionFlags = 0;
        mNextAppTransitionRequests.clear();
        setAppTransitionState(APP_STATE_RUNNING);
        //如果topOpeningApp不为空,即应用ActivityRecord不为空,
        //那么获取其ActivityRecord的容器,即应用Task
        final WindowContainer wc =
                topOpeningApp != null ? topOpeningApp.getAnimatingContainer() : null;
        //应用ActivityRecord不为空,获取AnimationAdapter,即RemoteAnimationAdapterWrapper
        final AnimationAdapter topOpeningAnim = wc != null ? wc.getAnimation() : null;

        //通知所有的AppTransitionListener,应用过渡动画即将开始
        //根据其返回值判断是否需要重新进行布局
        //我们这里所有的调用AppTransitionListener的redoLayout返回值都0
        //即redoLayout返回值都0
        int redoLayout = notifyAppTransitionStartingLocked(
                AppTransition.isKeyguardGoingAwayTransitOld(transit),
                AppTransition.isKeyguardOccludeTransitOld(transit),
                topOpeningAnim != null ? topOpeningAnim.getDurationHint() : 0,
                topOpeningAnim != null
                        ? topOpeningAnim.getStatusBarTransitionsStartTime()
                        : SystemClock.uptimeMillis(),
                AnimationAdapter.STATUS_BAR_TRANSITION_DURATION);
        //mRemoteAnimationController不为空
        if (mRemoteAnimationController != null) {
            mRemoteAnimationController.goodToGo(transit);
        }
         //如果mRemoteAnimationController为空
         //transit值为TRANSIT_OLD_WALLPAPER_CLOSE
         //topOpeningAnim,应用ActivityRecord不为空时
         else if ((isTaskOpenTransitOld(transit) || transit == TRANSIT_OLD_WALLPAPER_CLOSE)
                && topOpeningAnim != null) {
            //shouldAttachNavBarToAppDuringTransition(),在动画过渡期间将导航栏附加到应用程序
            //getRecentsAnimationController(),RecentsAnimationController为空
            if (mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
                    && mService.getRecentsAnimationController() == null) {
                //创建一个新的NavBarFadeAnimationController
                final NavBarFadeAnimationController controller =
                        new NavBarFadeAnimationController(mDisplayContent);
                // For remote animation case, the nav bar fades out and in is controlled by the
                // remote side. For non-remote animation case, we play the fade out/in animation
                // here. We play the nav bar fade-out animation when the app transition animation
                // starts and play the fade-in animation sequentially once the fade-out is finished.
                //使用fadeOutAndInSequentially方法使其淡出并淡入
                controller.fadeOutAndInSequentially(topOpeningAnim.getDurationHint(),
                        null /* fadeOutParent */, topOpeningApp.getSurfaceControl());
            }
        }
        return redoLayout;
    }

我们RemoteAnimationController已经创建,所以mRemoteAnimationController != null为true,进入该流程mRemoteAnimationController.goodToGo(transit);,调用RemoteAnimationController的goodToGo方法,传递的参数值为TRANSIT_OLD_WALLPAPER_CLOSE

准备开始动画

 /**
     * Called when the transition is ready to be started, and all leashes have been set up.
     */
    void goodToGo(@WindowManager.TransitionOldType int transit) {
        ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
        if (mCanceled) {
            ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
                    "goodToGo(): Animation canceled already");
            onAnimationFinished();
            invokeAnimationCancelled("already_cancelled");
            return;
        }

        // Scale the timeout with the animator scale the controlling app is using.
        mHandler.postDelayed(mTimeoutRunnable,
                (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
        mFinishedCallback = new FinishedCallback(this);

        // Create the app targets
        final RemoteAnimationTarget[] appTargets = createAppAnimations();
        if (appTargets.length == 0 && !AppTransition.isKeyguardOccludeTransitOld(transit)) {
            // Keyguard occlude transition can be executed before the occluding activity becomes
            // visible. Even in this case, KeyguardService expects to receive binder call, so we
            // don't cancel remote animation.
            ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
                    "goodToGo(): No apps to animate, mPendingAnimations=%d",
                    mPendingAnimations.size());
            onAnimationFinished();
            invokeAnimationCancelled("no_app_targets");
            return;
        }

        if (mOnRemoteAnimationReady != null) {
            mOnRemoteAnimationReady.run();
            mOnRemoteAnimationReady = null;
        }

        // Create the remote wallpaper animation targets (if any)
        final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();

        // Create the remote non app animation targets (if any)
        final RemoteAnimationTarget[] nonAppTargets = createNonAppWindowAnimations(transit);

        mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
            try {
                linkToDeathOfRunner();
                Slog.i("RemoteAnimationController","goodToGo mRemoteAnimationAdapter.getRunner():"+mRemoteAnimationAdapter.getRunner());
                ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): onAnimationStart,"
                                + " transit=%s, apps=%d, wallpapers=%d, nonApps=%d",
                        AppTransition.appTransitionOldToString(transit), appTargets.length,
                        wallpaperTargets.length, nonAppTargets.length);
                mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
                        wallpaperTargets, nonAppTargets, mFinishedCallback);
            } catch (RemoteException e) {
                Slog.e(TAG, "Failed to start remote animation", e);
                onAnimationFinished();
            }
            if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {
                ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");
                writeStartDebugStatement();
            }
        });
        setRunningRemoteAnimation(true);
    }

动画移除流程

处理和响应动画完成的逻辑

代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

		private void doAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
	        for (int i = 0; i < mSurfaceAnimationSources.size(); ++i) {
	            //mSurfaceAnimationSources中每个容器,做对应的onAnimationFinished
	            mSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim);
	        }
	        //清除动画源列表
	        mSurfaceAnimationSources.clear();
	        if (mDisplayContent != null) {
		        //调用DisplayContent的onWindowAnimationFinished方法
		        //从当前源码上看,主要是针对输入法相关做了一些操作
	            mDisplayContent.onWindowAnimationFinished(this, type);
	        }
	    }

		/**
		 * Called when an animation has finished running.
		 */
		protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
		       //主要用于 清空 mSurfaceAnimationSources 列表
		       doAnimationFinished(type, anim);
		       //WindowManagerService中实现onAnimationFinished()
		       //用于唤醒所有等待mGlobalLock对象的线程,确保多个线程能够正确地执行任务
		       mWmService.onAnimationFinished();
		       //将 mNeedsZBoost 设置为 false,表示不再需要Z轴增强
		       mNeedsZBoost = false;
		   }

我们这里mSurfaceAnimationSources是保存的是需要做动画的ActivityRecord,mSurfaceAnimationSources的值是在applyAnimationUnchecked方法中添加的。
mSurfaceAnimationSources.valueAt(i).onAnimationFinished(type, anim);调用了不同容器onAnimationFinished方法,在ActivityRecord和WindowState中都重写了这个方法。我们这里是远程动画,主要调用的就是ActivityRecord中重写的onAnimationFinished方法。
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

    @Override
    protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
        super.onAnimationFinished(type, anim);

        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");
        mTransit = TRANSIT_OLD_UNSET;
        mTransitFlags = 0;

        setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
                "ActivityRecord");

        clearThumbnail();
        setClientVisible(isVisible() || mVisibleRequested);

        getDisplayContent().computeImeTargetIfNeeded(this);

        ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s"
                + ": reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",
                this, reportedVisible, okToDisplay(), okToAnimate(),
                isStartingWindowDisplayed());

        // clean up thumbnail window
        if (mThumbnail != null) {
            mThumbnail.destroy();
            mThumbnail = null;
        }

        // WindowState.onExitAnimationDone might modify the children list, so make a copy and then
        // traverse the copy.
        final ArrayList<WindowState> children = new ArrayList<>(mChildren);
        children.forEach(WindowState::onExitAnimationDone);
        // The starting window could transfer to another activity after app transition started, in
        // that case the latest top activity might not receive exit animation done callback if the
        // starting window didn't applied exit animation success. Notify animation finish to the
        // starting window if needed.
        if (task != null && startingMoved) {
            final WindowState transferredStarting = task.getWindow(w ->
                    w.mAttrs.type == TYPE_APPLICATION_STARTING);
            if (transferredStarting != null && transferredStarting.mAnimatingExit
                    && !transferredStarting.isSelfAnimating(0 /* flags */,
                    ANIMATION_TYPE_WINDOW_ANIMATION)) {
                transferredStarting.onExitAnimationDone();
            }
        }

        getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
        scheduleAnimation();

        // Schedule to handle the stopping and finishing activities which the animation is done
        // because the activities which were animating have not been stopped yet.
        mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1461047.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Latent Diffusion Models / Stable Diffusion(LDM)

High-Resolution Image Synthesis with Latent Diffusion Models&#xff08;CVPR 2022&#xff09;https://arxiv.org/abs/2112.10752latent-diffusionstable-diffusion cross-attention&#xff08;用于多模态任务&#xff09; Cross-attention vs Self-attention&#xff…

外汇天眼:交易讲究时机,不要在这几个时间交易

每个交易者都想知道&#xff0c;什么时候是入场买卖的最好时机。 到底是1.1800入场呢&#xff1f; 还是等到1.1900&#xff1f; 但是&#xff0c;交易中不仅仅是关于从哪里入场&#xff0c;同样的&#xff0c;知道什么时候不去交易也是非常重要的。 这听起来像是一回事&#x…

python coding with ChatGPT 打卡第21天| 二叉树:最近公共祖先

相关推荐 python coding with ChatGPT 打卡第12天| 二叉树&#xff1a;理论基础 python coding with ChatGPT 打卡第13天| 二叉树的深度优先遍历 python coding with ChatGPT 打卡第14天| 二叉树的广度优先遍历 python coding with ChatGPT 打卡第15天| 二叉树&#xff1a;翻转…

网络原理 - HTTP/HTTPS(4)

HTTP响应详解 认识"状态码"(status code) 状态码表示访问一个页面的结果.(是访问成功,还是失败,还是其它的一些情况...).(响应结果如何) 学习状态码 -> 为了调试问题. 写服务器时,按照状态码的含义正确使用. 200 OK 这是最常见的状态码,表示访问成功. 抓包抓…

超详细!三款内衣洗衣机PK评测,希亦、觉飞、由利谁更胜一筹?

内衣裤作为我们日常必备的贴身衣物&#xff0c;所以对卫生方面的要求也比较高&#xff0c;但对许多人们而言&#xff0c;对内衣裤进行清洗是一项相当繁琐的事情&#xff0c;主要是因为并不能直接把内衣裤放入大型洗衣机里和其它衣服混合洗&#xff0c;所以大多数用户都会自己动…

不止于框架:全面深入Spring MVC,成就开发高手

介绍&#xff1a;Spring MVC是一个基于Java的轻量级Web框架&#xff0c;它实现了MVC&#xff08;Model-View-Controller&#xff09;设计模式。以下是对Spring MVC的详细介绍&#xff1a; MVC架构模式&#xff1a;Spring MVC遵循MVC架构模式的思想&#xff0c;将应用程序分为模…

几个常见的C/C++语言冷知识

当涉及到C/C语言时&#xff0c;有一些冷知识可能并不为人所熟知&#xff0c;但却可以让你更深入地理解这门古老而强大的编程语言。以下是一些有趣的C/C语言冷知识。 1. 数组的下标可以是负数 在我们日常的C语言编程中&#xff0c;数组是一个非常常见的数据结构。我们习惯性地使…

CSS 函数详解url、min、rgb、blur、scale、rotate、translate等

随着技术的不断进步&#xff0c;CSS 已经从简单的样式表发展成为拥有众多内置函数的强大工具。这些函数不仅增强了开发者的设计能力&#xff0c;还使得样式应用更加动态、灵活和响应式。本文将深入探讨 CSS 常见的 66 个函数&#xff0c;逐一剖析它们的功能和用法&#xff0c;一…

Linux目录:scp命令

目录 1 scp命令1.1简介1.2命令说明1.3 实例1、从本地复制的远程2、从远程复制到本地3、-r&#xff1a;复制目录4、 -p&#xff1a;保留原文件的修改时间&#xff0c;访问时间5、-P&#xff1a;指定传输数据的端口6、扩展&#xff1a;修改ssh默认端口 1 scp命令 1.1简介 scp 是…

如何在Shopee平台上选择爆款商品:借鉴爆款属性的有效策略

在当今激烈竞争的电商市场中&#xff0c;想要在Shopee平台上取得成功&#xff0c;卖家需要精心选择潜在的热销产品。借鉴爆款商品的属性是一种行之有效的策略&#xff0c;能够帮助卖家快速找到市场上的热门商品。通过分析市场趋势、竞品表现、社交媒体趋势等多方面因素&#xf…

单片机学习笔记---红外遥控(外部中断)

目录 红外遥控简介 硬件电路 基本发送与接收 NEC编码​​​​​​​ 遥控器键码 复习外部中断和定时器 红外遥控简介 红外遥控是利用红外光进行通信的设备&#xff0c;由红外LED将调制后的信号发出&#xff0c;由专用的红外接收头进行解调输出 通信方式&#xff1a;单工…

String字符串,FastJson常用操作方法

JSON字符串操作 1、创建配置环境 # 引入测试包testImplementation group: org.springframework.boot, name: spring-boot-starter-test, version: 2.2.6.RELEASE # 创建测试类RunWith(SpringRunner.class)SpringBootTestpublic class JsonTest {Testpublic void test(){Syste…

13.Qt 文件的读和写,样式表文件的读用

目录 前言&#xff1a; 技能&#xff1a; 内容&#xff1a; 1. 界面 2.信号槽 ①浏览按键 ②保存按键 ③加载样式按键 参考&#xff1a; 前言&#xff1a; 上一篇文章说明了如何弹窗选取文件并在Qlabel中显示文件内容 12.QT文件对话框 文件的弹窗选择-QFileDialog 这篇…

Gitlab CI 报错: /usr/bin/bash: line 137: kubectl: command not found

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

【 Maven 】花式玩法之多模块项目

目录 一、认识Maven多模块项目 二、maven如何定义项目的发布策略 2.1 版本管理 2.2 构建配置 2.3 部署和发布 2.4 依赖管理 2.5 发布流程 三、使用Jenkins持续集成Maven项目 四、总结 如果你有一个多模块项目&#xff0c;并且想将这些模块发布到不同的仓库或目标位置&…

机器学习基础(四)非监督学习的进阶探索

导语&#xff1a;上一节我们详细探索监督学习的进阶应用&#xff0c;详情可见&#xff1a; 机器学习基础&#xff08;三&#xff09;监督学习的进阶探索-CSDN博客文章浏览阅读296次&#xff0c;点赞13次&#xff0c;收藏11次。监督学习作为机器学习的一个主要分支&#xff0c;…

34.仿简道云公式函数实战-数学函数-PRODUCT

1. PRODUCT函数 函数使所有以参数形式给出的数字相乘并返回乘积。 2. 函数用法 PRODUCT(number1, [number2], …) 3. 函数示例 PRODUCT(数量, 单价) 4. 代码实战 首先我们在function包下创建math包&#xff0c;在math包下创建ProductFunction类&#xff0c;代码如下&…

x86与arm架构区别

前言 我们经常讨论的芯片的X86架构和ARM架构&#xff0c;那么他们的的区别什么呢&#xff1f;其实俩种都是主流的两种计算机处理器架构&#xff08;CPU&#xff09;架构&#xff0c;X86架构的CPU是PC服务器行业的老大&#xff0c;ARM架构的CPU则是移动端的老大。X86架构和arm架…

如何在Shopee 上选择热销商品?shopee应该在哪选品

在如今激烈竞争的电商市场中&#xff0c;如何通过精准的选品策略提升在Shopee平台上的销售业绩成为卖家们关注的焦点。Shopee作为一个蓬勃发展的电商平台&#xff0c;提供了多种资源和工具来帮助卖家做出明智的选品决策。通过深入了解这些渠道和策略&#xff0c;卖家们可以更好…

云服务器ECS价格表出炉——阿里云

2024年阿里云服务器租用价格表更新&#xff0c;云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、ECS u1实例2核4G、5M固定带宽、80G ESSD Entry盘优惠价格199元一年&#xff0c;轻量应用服务器2核2G3M带宽轻量服务器一年61元、2核4G4M带宽轻量服务器一年165元12个月、2核4G服…