Android T 远程动画显示流程其三——桌面侧动画启动到系统侧结束流程

前言

接着前文分析Android T 远程动画显示流程其二
我们通过IRemoteAnimationRunner跨进程通信从系统进程来到了桌面进程,这里是真正动画播放的逻辑。
之后又通过IRemoteAnimationFinishedCallback跨进程通信回到系统进程,处理动画结束时的逻辑。

进入桌面进程启动动画

跨进程通信,实现IRemoteAnimationRunner

代码路径:frameworks/base/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationRunnerCompat.java

public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner.Stub {

    public abstract void onAnimationStart(@WindowManager.TransitionOldType int transit,
            RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
            RemoteAnimationTarget[] nonApps, Runnable finishedCallback);

    @Override
    public final void onAnimationStart(@TransitionOldType int transit,
            RemoteAnimationTarget[] apps,
            RemoteAnimationTarget[] wallpapers,
            RemoteAnimationTarget[] nonApps,
            final IRemoteAnimationFinishedCallback finishedCallback) {
        //调用自身抽象方法onAnimationStart
        onAnimationStart(transit, apps, wallpapers,
                nonApps, () -> {
                    try {
                        finishedCallback.onAnimationFinished();
                    } catch (RemoteException e) {
                        Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
                                + " finished callback", e);
                    }
                });
    }
    ......
}

这里传递的参数都是前面RemoteAnimationController.goodToGo方法中获取的值。
transit的值是TRANSIT_OLD_WALLPAPER_CLOSE(12);
app指的是桌面和应用的RemoteAnimationTarget;
wallpapers壁纸的RemoteAnimationTarget;
nonApp非APP类型的RemoteAnimationTarget;
finishedCallback是FinishedCallback对象,这里传递的是调用了其onAnimationFinished()方法。

这方方法调用了自身抽象方法调用自身抽象方法onAnimationStart,onAnimationStart方法真正的实现在LauncherAnimationRunner类中

@TargetApi(Build.VERSION_CODES.P)
public class LauncherAnimationRunner extends RemoteAnimationRunnerCompat {
    ......
    @BinderThread
    public void onAnimationStart(
            int transit,
            RemoteAnimationTarget[] appTargets,
            RemoteAnimationTarget[] wallpaperTargets,
            RemoteAnimationTarget[] nonAppTargets,
            Runnable runnable) {
        Runnable r = () -> {
            //退出动画的流程,此时mAnimationResult为空,尚未进入该流程
            finishExistingAnimation();
            //创建AnimationResult,传递了两个runnable
            //() -> mAnimationResult = null,把AnimationResult对象置空
            //runnable,就是前面传递的IRemoteAnimationFinishedCallback.onAnimationFinished
            mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
            //传递从系统侧调用过来的参数创建动画
            getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
                    mAnimationResult);
        };
        //根据mStartAtFrontOfQueue的值,执行线程 r
        if (mStartAtFrontOfQueue) {
            //将Runnable插入到消息队列的前面,以确保它尽快被执行
            postAtFrontOfQueueAsynchronously(mHandler, r);
        } else {
            //将Runnable异步地插入到消息队列中,它将在队列中的其他消息之后执行。
            postAsyncCallback(mHandler, r);
        }
    }
    ......
}
  • 退出动画的流程
    finishExistingAnimation();

        @UiThread
        private void finishExistingAnimation() {
            if (mAnimationResult != null) {
                mAnimationResult.finish();
                mAnimationResult = null;
            }
        }
    

    根据mAnimationResult是否为空执行finish方法,主要就是执行mASyncFinishRunnable,后续会在动画退出流程中细讲finish方法。

  • 创建AnimationResult
    mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);

        public static final class AnimationResult {
            ......
            private AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable) {
                mSyncFinishRunnable = syncFinishRunnable;
                mASyncFinishRunnable = asyncFinishRunnable;
            }
            ......
        }
    

    AnimationResult主要用来返回当前动画播放结果,以便后续执行动画播放完成时的回调(mASyncFinishRunnable)。
    () -> mAnimationResult = null,一个把AnimationResult对象置空的Runnable,保存到mSyncFinishRunnable中;
    runnable,就是前面传递的IRemoteAnimationFinishedCallback.onAnimationFinished,保存到mASyncFinishRunnable中。

  • 传递从系统侧创建的参数创建动画

    getFactory().onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
                        mAnimationResult);
    

    传递了从系统侧创建的参数,并传递了mAnimationResult对象。这里调用的是RemoteAnimationFactory接口中的onCreateAnimation方法。

        /**
         * Used with LauncherAnimationRunner as an interface for the runner to call back to the
         * implementation.
         */
        @FunctionalInterface
        public interface RemoteAnimationFactory {
    
            /**
             * Called on the UI thread when the animation targets are received. The implementation must
             * call {@link AnimationResult#setAnimation} with the target animation to be run.
             */
            void onCreateAnimation(int transit,
                    RemoteAnimationTarget[] appTargets,
                    RemoteAnimationTarget[] wallpaperTargets,
                    RemoteAnimationTarget[] nonAppTargets,
                    LauncherAnimationRunner.AnimationResult result);
            ......
        }
    

    在最开始Launcher.startActivitySafely流程中,QuickstepTransitionManager.getActivityLaunchOptions方法中创建了AppLaunchAnimationRunner对象,并作为RemoteAnimationFactory对象传递到了。

            mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);
            RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(
                    mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */);
    

    因此我们这里RemoteAnimationFactory的实现,就是在QuickstepTransitionManager.AppLaunchAnimationRunner中。

传递从系统侧创建的参数创建动画

代码路径:packages/apps/Launcher3/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java

    private class AppLaunchAnimationRunner implements RemoteAnimationFactory {

        private final View mV;
        private final RunnableList mOnEndCallback;

        AppLaunchAnimationRunner(View v, RunnableList onEndCallback) {
            mV = v;
            mOnEndCallback = onEndCallback;
        }

        @Override
        public void onCreateAnimation(int transit,
                RemoteAnimationTarget[] appTargets,
                RemoteAnimationTarget[] wallpaperTargets,
                RemoteAnimationTarget[] nonAppTargets,
                LauncherAnimationRunner.AnimationResult result) {
            //创建AnimatorSet
            AnimatorSet anim = new AnimatorSet();
            //判断桌面的是否已经不在前台
            boolean launcherClosing =
                    launcherIsATargetWithMode(appTargets, MODE_CLOSING);

            //检查是否从桌面小部件启动应用
            final boolean launchingFromWidget = mV instanceof LauncherAppWidgetHostView;
            //检查是否从最近应用列表启动应用
            final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
            //决定是否跳过动画的第一帧
            final boolean skipFirstFrame;
            if (launchingFromWidget) {//从桌面小部件启动应用的动画
                composeWidgetLaunchAnimator(anim, (LauncherAppWidgetHostView) mV, appTargets,
                        wallpaperTargets, nonAppTargets, launcherClosing);
                addCujInstrumentation(
                        anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_WIDGET);
                skipFirstFrame = true;
            } else if (launchingFromRecents) {//从最近任务启动应用的动画
                composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
                        launcherClosing);
                addCujInstrumentation(
                        anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS);
                skipFirstFrame = true;
            } else {//点击桌面图标启动应用的动画
                composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
                        launcherClosing);
                addCujInstrumentation(anim, InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_ICON);
                skipFirstFrame = false;
            }

            //桌面不在前台给动画添加一个监听器
            if (launcherClosing) {
                anim.addListener(mForceInvisibleListener);
            }
            //设置动画和回调
            result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
                    skipFirstFrame);
        }

        @Override
        public void onAnimationCancelled() {
            mOnEndCallback.executeAllAndDestroy();
        }
    }

这里我们主要关注点击桌面图标启动应用的动画逻辑

点击桌面图标启动应用的动画

composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets, nonAppTargets,
                        launcherClosing);

anim一个AnimatorSet对象;
mV这里指的是启动的应用图标,比如com.android.launcher3.BubbleTextView{bace738 VFED..CL. ........ 582,525-859,945 #7f09016a app:id/icon}
appTargets指的是桌面和应用的RemoteAnimationTarget;
wallpaperTargets壁纸的RemoteAnimationTarget;
nonAppTargets非APP类型的RemoteAnimationTarget;
launcherClosing此时桌面的是否已经不在前台,因此值为true

    /**
     * Compose the animations for a launch from the app icon.
     *
     * @param anim            the animation to add to
     * @param v               the launching view with the icon
     * @param appTargets      the list of opening/closing apps
     * @param launcherClosing true if launcher is closing
     */
    private void composeIconLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
            @NonNull RemoteAnimationTarget[] appTargets,
            @NonNull RemoteAnimationTarget[] wallpaperTargets,
            @NonNull RemoteAnimationTarget[] nonAppTargets,
            boolean launcherClosing) {
        // Set the state animation first so that any state listeners are called
        // before our internal listeners.
        //setCurrentAnimation(anim)取消任何正在运行的动画,设置新的动画
        //即将动画设置为当前状态动画
        mLauncher.getStateManager().setCurrentAnimation(anim);

        // Note: the targetBounds are relative to the launcher
        int startDelay = getSingleFrameMs(mLauncher);
        //设置动画参数
        Animator windowAnimator = getOpeningWindowAnimators(
                v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing);
        //设置动画启动延时
        windowAnimator.setStartDelay(startDelay);
        //设置windowAnimator给AnimatorSet对象
        anim.play(windowAnimator);
        //如果桌面已经不在最顶层显示
        if (launcherClosing) {
            // Delay animation by a frame to avoid jank.
            //将动画延迟一帧以避免抖动
            //创建一个launcherAnimator动画和endListener线程
            Pair<AnimatorSet, Runnable> launcherContentAnimator =
                    getLauncherContentAnimator(true /* isAppOpening */, startDelay, false);
            //把launcherAnimator动画放到AnimatorSet
            anim.play(launcherContentAnimator.first);
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    //运行endListener线程
                    launcherContentAnimator.second.run();
                }
            });
        }
    }

之前最为关键的就是getOpeningWindowAnimators方法

Animator windowAnimator = getOpeningWindowAnimators(
                v, appTargets, wallpaperTargets, nonAppTargets, launcherClosing);

这个方法是动画真正的设置部分

设置动画相关参数、监听等

    /**
     * @return Animator that controls the window of the opening targets from app icons.
     */
    private Animator getOpeningWindowAnimators(View v,
            RemoteAnimationTarget[] appTargets,
            RemoteAnimationTarget[] wallpaperTargets,
            RemoteAnimationTarget[] nonAppTargets,
            boolean launcherClosing) {
        //获取应用方向
        int rotationChange = getRotationChange(appTargets);
        //获取启动应用的窗口边界
        Rect windowTargetBounds = getWindowTargetBounds(appTargets, rotationChange);
        //检查appTargets中所有应用目标是否半透明
        //areAllTargetsTranslucent方法返回的的是,
        //mode值为MODE_OPENING(正在打开的应用)的RemoteAnimationTarget的isTranslucent的值
        boolean appTargetsAreTranslucent = areAllTargetsTranslucent(appTargets);

        RectF launcherIconBounds = new RectF();
        //获取一个浮动图标视图
        FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
                !appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);
        Rect crop = new Rect();
        Matrix matrix = new Matrix();

        //创建mMode为MODE_OPENING的RemoteAnimationTargets对象
        //把app、壁纸和非app类型的RemoteAnimationTarget对象保存到RemoteAnimationTargets中
        RemoteAnimationTargets openingTargets = new RemoteAnimationTargets(appTargets,
                wallpaperTargets, nonAppTargets, MODE_OPENING);
        //创建SurfaceTransactionApplier对象
        SurfaceTransactionApplier surfaceApplier =
                new SurfaceTransactionApplier(floatingView);
        //为了确保动画完成时,释放相关资源
        openingTargets.addReleaseCheck(surfaceApplier);
        //获取导航栏的RemoteAnimationTarget对象
        RemoteAnimationTarget navBarTarget = openingTargets.getNavBarRemoteAnimationTarget();

        //DragLayer是一个ViewGroup,协调处理它的子view拖动的容器
        //getLocationOnScreen获取DragLayer在屏幕上的绝对位置
        int[] dragLayerBounds = new int[2];
        mDragLayer.getLocationOnScreen(dragLayerBounds);

        //检查是否支持冷启动窗口Splash Screen
        final boolean hasSplashScreen;
        if (supportsSSplashScreen()) {
            int taskId = openingTargets.getFirstAppTargetTaskId();
            Pair<Integer, Integer> defaultParams = Pair.create(STARTING_WINDOW_TYPE_NONE, 0);
            Pair<Integer, Integer> taskParams =
                    mTaskStartParams.getOrDefault(taskId, defaultParams);
            mTaskStartParams.remove(taskId);
            hasSplashScreen = taskParams.first == STARTING_WINDOW_TYPE_SPLASH_SCREEN;
        } else {
            hasSplashScreen = false;
        }

        //创建AnimOpenProperties对象,设置应用启动时的动画属性
        AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile,
                windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1],
                hasSplashScreen, floatingView.isDifferentFromAppIcon());
        //计算裁剪区域的边界
        int left = prop.cropCenterXStart - prop.cropWidthStart / 2;
        int top = prop.cropCenterYStart - prop.cropHeightStart / 2;
        int right = left + prop.cropWidthStart;
        int bottom = top + prop.cropHeightStart;
        // Set the crop here so we can calculate the corner radius below.
        crop.set(left, top, right, bottom);

        //创建临时矩形和点对象
        RectF floatingIconBounds = new RectF();
        RectF tmpRectF = new RectF();
        Point tmpPos = new Point();

        //设置动画的一些参数和监听
        AnimatorSet animatorSet = new AnimatorSet();
        ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
        appAnimator.setDuration(APP_LAUNCH_DURATION);
        //设置动画的插值器为LINEAR。插值器决定了动画的速度曲线。LINEAR意味着动画将匀速进行
        appAnimator.setInterpolator(LINEAR);
        //为appAnimator添加一个动画监听器floatingView。
        //当动画开始、结束、取消或重复时,floatingView上的相应方法将被调用。
        appAnimator.addListener(floatingView);
        appAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            //监听动开始
            public void onAnimationStart(Animator animation) {
                //获取LauncherTaskbarUIController的实例
                LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
                //检查是否应该调用shouldShowEdu()
                if (taskbarController != null && taskbarController.shouldShowEdu()) {
                    // LAUNCHER_TASKBAR_EDUCATION_SHOWING is set to true here, when the education
                    // flow is about to start, to avoid a race condition with other components
                    // that would show something else to the user as soon as the app is opened.
                    //将LAUNCHER_TASKBAR_EDUCATION_SHOWING设置为true,以避免与其他组件发生竞争
                    Settings.Secure.putInt(mLauncher.getContentResolver(),
                            LAUNCHER_TASKBAR_EDUCATION_SHOWING, 1);
                }
            }

            @Override
            //监听动结束
            public void onAnimationEnd(Animator animation) {
                if (v instanceof BubbleTextView) {
                    //我们这里v是BubbleTextView类型
                    //设置控件v保持按下的状态为false
                    ((BubbleTextView) v).setStayPressed(false);
                }
                //获取LauncherTaskbarUIController的实例
                LauncherTaskbarUIController taskbarController = mLauncher.getTaskbarUIController();
                if (taskbarController != null) {
                    //调用shouldShowEdu()
                    taskbarController.showEdu();
                }
                //释放所有类型的RemoteAnimationTarget对象
                //包含壁纸、app和非app类型的RemoteAnimationTarget对象
                openingTargets.release();
            }
        });
        
        //initialWindowRadius用于设置动画开始时的窗口圆角半径
        //supportsRoundedCornersOnWindows(mLauncher.getResources()判断桌面是否支持窗口圆角
        final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
                ? Math.max(crop.width(), crop.height()) / 2f
                : 0f;
        //finalWindowRadius用于设置动画结束时的窗口圆角半径
        //mDeviceProfile.isMultiWindowMode检查是否处于多窗口模式
        //getWindowCornerRadius(mLauncher)获取桌面窗口的圆角半径
        final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
                ? 0 : getWindowCornerRadius(mLauncher);
        //inalShadowRadius用于设置动画结束时的阴影半径
        //appTargetsAreTranslucent表示应用目标是否半透明
        //mMaxShadowRadius最大阴影半径值
        final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;
        
        MultiValueUpdateListener listener = new MultiValueUpdateListener() {
            //mDx:这个属性表示在动画过程中,X轴上的位移变化。
            //它从0开始,到prop.dX结束,动画时长为APP_LAUNCH_DURATION,使用mOpeningXInterpolator作为插值器。
            FloatProp mDx = new FloatProp(0, prop.dX, 0, APP_LAUNCH_DURATION,
                    mOpeningXInterpolator);
            //这个属性表示在动画过程中,Y轴上的位移变化。
            //它从0开始,到prop.dY结束,动画时长为APP_LAUNCH_DURATION,使用mOpeningInterpolator作为插值器。
            FloatProp mDy = new FloatProp(0, prop.dY, 0, APP_LAUNCH_DURATION,
                    mOpeningInterpolator);
            
            //mIconScaleToFitScreen:这个属性表示应用图标在屏幕上的缩放变化。
            //它从prop.initialAppIconScale开始,到prop.finalAppIconScale结束,
            //动画时长为APP_LAUNCH_DURATION,使用mOpeningInterpolator作为插值器。
            FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
                    prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, mOpeningInterpolator);
            //mIconAlpha:这个属性表示应用图标的透明度变化。
            //它从prop.iconAlphaStart开始,到0结束,
            //动画的开始延迟为APP_LAUNCH_ALPHA_START_DELAY,时长为APP_LAUNCH_ALPHA_DURATION,
            //使用线性插值器(LINEAR)。
            FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f,
                    APP_LAUNCH_ALPHA_START_DELAY, APP_LAUNCH_ALPHA_DURATION, LINEAR);

            //mWindowRadius:这个属性表示窗口圆角的半径变化。
            //它从initialWindowRadius开始,到finalWindowRadius结束,动画时长为APP_LAUNCH_DURATION,
            //使用mOpeningInterpolator作为插值器。
            FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius, 0,
                    APP_LAUNCH_DURATION, mOpeningInterpolator);
            //mShadowRadius:这个属性表示阴影的半径变化。
            //它从0开始,到finalShadowRadius结束,动画时长为APP_LAUNCH_DURATION,
            //使用mOpeningInterpolator作为插值器。
            FloatProp mShadowRadius = new FloatProp(0, finalShadowRadius, 0,
                    APP_LAUNCH_DURATION, mOpeningInterpolator);
 
            //mCropRectCenterX、mCropRectCenterY、mCropRectWidth、mCropRectHeight
            //这些属性分别表示裁剪矩形的中心X坐标、中心Y坐标、宽度和高度的变化。
            //它们都有各自的起始值和结束值,动画时长为APP_LAUNCH_DURATION,使用mOpeningInterpolator作为插值器。
            FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd,
                    0, APP_LAUNCH_DURATION, mOpeningInterpolator);
            FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
                    0, APP_LAUNCH_DURATION, mOpeningInterpolator);
            FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd, 0,
                    APP_LAUNCH_DURATION, mOpeningInterpolator);
            FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0,
                    APP_LAUNCH_DURATION, mOpeningInterpolator);

            //这个属性表示导航栏的淡出效果。
            //它从1开始,到0结束,动画时长为ANIMATION_NAV_FADE_OUT_DURATION,
            //使用NAV_FADE_OUT_INTERPOLATOR作为插值器。
            FloatProp mNavFadeOut = new FloatProp(1f, 0f, 0, ANIMATION_NAV_FADE_OUT_DURATION,
                    NAV_FADE_OUT_INTERPOLATOR);
            //mNavFadeIn:这个属性表示导航栏的淡入效果。它从0开始,到1结束,
            //动画的开始延迟为ANIMATION_DELAY_NAV_FADE_IN,时长为ANIMATION_NAV_FADE_IN_DURATION,
            //使用NAV_FADE_IN_INTERPOLATOR作为插值器。
            FloatProp mNavFadeIn = new FloatProp(0f, 1f, ANIMATION_DELAY_NAV_FADE_IN,
                    ANIMATION_NAV_FADE_IN_DURATION, NAV_FADE_IN_INTERPOLATOR);
             
			//动画的更新
            @Override
            public void onUpdate(float percent, boolean initOnly) {
                // Calculate the size of the scaled icon.
				//计算缩放图标的大小
                float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value;
                float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value;

                int left = (int) (mCropRectCenterX.value - mCropRectWidth.value / 2);
                int top = (int) (mCropRectCenterY.value - mCropRectHeight.value / 2);
                int right = (int) (left + mCropRectWidth.value);
                int bottom = (int) (top + mCropRectHeight.value);
                crop.set(left, top, right, bottom);

                final int windowCropWidth = crop.width();
                final int windowCropHeight = crop.height();
                if (rotationChange != 0) {
                    Utilities.rotateBounds(crop, mDeviceProfile.widthPx,
                            mDeviceProfile.heightPx, rotationChange);
                }

                // Scale the size of the icon to match the size of the window crop.
				//缩放图标的大小以匹配窗口裁剪的大小。
                float scaleX = iconWidth / windowCropWidth;
                float scaleY = iconHeight / windowCropHeight;
                float scale = Math.min(1f, Math.max(scaleX, scaleY));

                float scaledCropWidth = windowCropWidth * scale;
                float scaledCropHeight = windowCropHeight * scale;
                float offsetX = (scaledCropWidth - iconWidth) / 2;
                float offsetY = (scaledCropHeight - iconHeight) / 2;

                // Calculate the window position to match the icon position.
				//计算窗口位置以匹配图标位置。
                tmpRectF.set(launcherIconBounds);
                tmpRectF.offset(dragLayerBounds[0], dragLayerBounds[1]);
                tmpRectF.offset(mDx.value, mDy.value);
                Utilities.scaleRectFAboutCenter(tmpRectF, mIconScaleToFitScreen.value);
                float windowTransX0 = tmpRectF.left - offsetX - crop.left * scale;
                float windowTransY0 = tmpRectF.top - offsetY - crop.top * scale;

                // Calculate the icon position.
				//计算图标位置
                floatingIconBounds.set(launcherIconBounds);
                floatingIconBounds.offset(mDx.value, mDy.value);
                Utilities.scaleRectFAboutCenter(floatingIconBounds, mIconScaleToFitScreen.value);
                floatingIconBounds.left -= offsetX;
                floatingIconBounds.top -= offsetY;
                floatingIconBounds.right += offsetX;
                floatingIconBounds.bottom += offsetY;

                if (initOnly) {
                    // For the init pass, we want full alpha since the window is not yet ready.
                    //使用floatingView.update方法更新浮动视图的属性,包括透明度、边界、半径等。  
                    floatingView.update(1f, 255, floatingIconBounds, percent, 0f,
                            mWindowRadius.value * scale, true /* isOpening */);
                    return;
                }

                SurfaceTransaction transaction = new SurfaceTransaction();
                
                //遍历桌面和启动应用的RemoteAnimationTarget,获取其leash,分别做处理
                for (int i = appTargets.length - 1; i >= 0; i--) {
                    RemoteAnimationTarget target = appTargets[i];
                    SurfaceProperties builder = transaction.forSurface(target.leash);

                    if (target.mode == MODE_OPENING) {
                    /**
                     * 如果目标模式是MODE_OPENING(打开模式),代码会设置一个矩阵(matrix)来进行缩放和平移操作。
                     * 根据rotationChange的值(可能是表示屏幕旋转的变量),代码会决定如何平移窗口。  
                     * 然后,使用floatingView.update方法更新浮动视图的属性,包括透明度、边界、半径等。  
                     * 接着,通过builder.setMatrix等方法设置窗口的矩阵、裁剪区域、透明度、圆角半径和阴影半径。
                     */
                        matrix.setScale(scale, scale);
                        if (rotationChange == 1) {
                            matrix.postTranslate(windowTransY0,
                                    mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth));
                        } else if (rotationChange == 2) {
                            matrix.postTranslate(
                                    mDeviceProfile.widthPx - (windowTransX0 + scaledCropWidth),
                                    mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight));
                        } else if (rotationChange == 3) {
                            matrix.postTranslate(
                                    mDeviceProfile.heightPx - (windowTransY0 + scaledCropHeight),
                                    windowTransX0);
                        } else {
                            matrix.postTranslate(windowTransX0, windowTransY0);
                        }

                        floatingView.update(mIconAlpha.value, 255, floatingIconBounds, percent, 0f,
                                mWindowRadius.value * scale, true /* isOpening */);
                        builder.setMatrix(matrix)
                                .setWindowCrop(crop)
                                .setAlpha(1f - mIconAlpha.value)
                                .setCornerRadius(mWindowRadius.value)
                                .setShadowRadius(mShadowRadius.value);
                    } else if (target.mode == MODE_CLOSING) {
                    /**
                     * 如果目标模式是MODE_CLOSING(关闭模式),代码会处理关闭动画。
                     * 首先,根据目标的本地边界或位置设置临时位置(tmpPos)。
                     * 然后,根据rotationChange的值,可能需要对裁剪区域(crop)和临时位置进行旋转调整。
                     * 最后,设置窗口的矩阵和裁剪区域,并将透明度设置为1(完全不透明)。
                     */
                        if (target.localBounds != null) {
                            tmpPos.set(target.localBounds.left, target.localBounds.top);
                        } else {
                            tmpPos.set(target.position.x, target.position.y);
                        }
                        final Rect crop = new Rect(target.screenSpaceBounds);
                        crop.offsetTo(0, 0);

                        if ((rotationChange % 2) == 1) {
                            int tmp = crop.right;
                            crop.right = crop.bottom;
                            crop.bottom = tmp;
                            tmp = tmpPos.x;
                            tmpPos.x = tmpPos.y;
                            tmpPos.y = tmp;
                        }
                        matrix.setTranslate(tmpPos.x, tmpPos.y);
                        builder.setMatrix(matrix)
                                .setWindowCrop(crop)
                                .setAlpha(1f);
                    }
                }

                /**
                 * 如果navBarTarget不为空(即存在导航栏目标),代码会为其设置动画和视图属性。 
                 * 根据`mNavFadeIn.value`的值,决定是淡入还是淡出导航栏。如果淡入值大于起始值,则应用淡入动画;
                 */
                if (navBarTarget != null) {
                    SurfaceProperties navBuilder =
                            transaction.forSurface(navBarTarget.leash);
                    if (mNavFadeIn.value > mNavFadeIn.getStartValue()) {
                        matrix.setScale(scale, scale);
                        matrix.postTranslate(windowTransX0, windowTransY0);
                        navBuilder.setMatrix(matrix)
                                .setWindowCrop(crop)
                                .setAlpha(mNavFadeIn.value);
                    } else {
                        navBuilder.setAlpha(mNavFadeOut.value);
                    }
                }
                surfaceApplier.scheduleApply(transaction);
            }
        };
        appAnimator.addUpdateListener(listener);
        // Since we added a start delay, call update here to init the FloatingIconView properly.
        //调用MultiValueUpdateListener.update更新动画显示
        listener.onUpdate(0, true /* initOnly */);

        // If app targets are translucent, do not animate the background as it causes a visible
        // flicker when it resets itself at the end of its animation.
        //appTargetsAreTranslucent,启动的应用为半透明
        //或 !launcherClosing,桌面在最顶层
        if (appTargetsAreTranslucent || !launcherClosing) {
            //仅设置appAnimator给animatorSet
            animatorSet.play(appAnimator);
        } else {
            //设置appAnimator和getBackgroundAnimator() (背景动画)
            //用于并行播放
            animatorSet.playTogether(appAnimator, getBackgroundAnimator());
        }
        return animatorSet;
    }

设置一些动画相关参数和监听,通过MultiValueUpdateListener.update方法更新动画显示。

调用setAnimation设置动画和回调

回到QuickstepTransitionManager.AppLaunchAnimationRunner.onCreateAnimation方法中,继续看到setAnimation方法:

result.setAnimation(anim, mLauncher, mOnEndCallback::executeAllAndDestroy,
                    skipFirstFrame);

前面的在getOpeningWindowAnimators方法中设置的动画,通过anim播放

动画的启动与结束

代码路径:packages/apps/Launcher3/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java

        /**
         * Sets the animation to play for this app launch
         * @param skipFirstFrame Iff true, we skip the first frame of the animation.
         *                       We set to false when skipping first frame causes jank.
         */
        @UiThread
        public void setAnimation(AnimatorSet animation, Context context,
                @Nullable Runnable onCompleteCallback, boolean skipFirstFrame) {
            if (mInitialized) {
                throw new IllegalStateException("Animation already initialized");
            }
            mInitialized = true;
            mAnimator = animation;
            mOnCompleteCallback = onCompleteCallback;
            //如果动画为空,直接调用finish方法,走结束动画流程
            if (mAnimator == null) {
                finish();
            } else if (mFinished) {//mFinished为true,表示动画播放结束
                // Animation callback was already finished, skip the animation.
                //调用mAnimator.start()和mAnimator.end()来跳过动画
                mAnimator.start();
                mAnimator.end();
                if (mOnCompleteCallback != null) {
                    mOnCompleteCallback.run();
                }
            } else {
                // Start the animation
                //添加动画监听
                mAnimator.addListener(new AnimatorListenerAdapter() {
                    @Override
                    //动画结束时的监听,调用finish()方法
                    public void onAnimationEnd(Animator animation) {
                        finish();
                    }
                });
                //开始播放动画
                mAnimator.start();
                
                //如果skipFirstFrame为true
                if (skipFirstFrame) {
                    // Because t=0 has the app icon in its original spot, we can skip the
                    // first frame and have the same movement one frame earlier.
                    //调用mAnimator.setCurrentPlayTime()来设置动画的当前播放时间,
                    //该时间为动画总时长与getSingleFrameMs(context)的较小值。
                    //这可以使得应用图标从原始位置开始的移动提前一帧,
                    //因为t=0时应用图标位于其原始位置。
                    mAnimator.setCurrentPlayTime(
                            Math.min(getSingleFrameMs(context), mAnimator.getTotalDuration()));
                }
            }
        }
    }

这个方法主要是通过mAnimator.start();启动动画的播放。当动画播放结束时,使用finish();方法进入动画结束播放流程。

动画播放结束

动画播放结束时,调用finish方法进入结束动画流程

代码路径:packages/apps/Launcher3/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java

    public static final class AnimationResult {
        ......
        @UiThread
        private void finish() {
            if (!mFinished) {
                //运行的是 () -> mAnimationResult = null
                //即把AnimationResult对象置空
                mSyncFinishRunnable.run();
                UI_HELPER_EXECUTOR.execute(() -> {
                    //运行的是IRemoteAnimationFinishedCallback.onAnimationFinished
                    mASyncFinishRunnable.run();
                    if (mOnCompleteCallback != null) {
                        MAIN_EXECUTOR.execute(mOnCompleteCallback);
                    }
                });
                //mFinished标志位置为true,表示动画播放完成。
                mFinished = true;
            }
        }
        ......
    }

前面跨进程通信时,对AnimationResult构造方法进行了初始化

private AnimationResult(Runnable syncFinishRunnable, Runnable asyncFinishRunnable) {
            mSyncFinishRunnable = syncFinishRunnable;
            mASyncFinishRunnable = asyncFinishRunnable;
        }

并且onAnimationStart方法中给创建了AnimationResult对象,传递了两个runnable。
mAnimationResult = new AnimationResult(() -> mAnimationResult = null, runnable);
这里传递的runnable就是跨进程通信传递过来的动画完成时回调。

mSyncFinishRunnable表示的就是() -> mAnimationResult = null,置空AnimationResult对象;
mASyncFinishRunnable表示的就是IRemoteAnimationFinishedCallback.onAnimationFinished方法,即跨进程调用结束动画流程。

跨进程通信进入动画结束流程

代码路径:frameworks/base/core/java/android/view/IRemoteAnimationFinishedCallback.aidl

/**
 * Interface to be invoked by the controlling process when a remote animation has finished.
 *
 * @see IRemoteAnimationRunner
 * {@hide}
 */
oneway interface IRemoteAnimationFinishedCallback {
    @UnsupportedAppUsage
    void onAnimationFinished();
}

IRemoteAnimationFinishedCallback的实现在RemoteAnimationController.FinishedCallback类中

进入系统进程结束动画

跨进程通信,实现IRemoteAnimationFinishedCallback

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

    private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {

        RemoteAnimationController mOuter;

        FinishedCallback(RemoteAnimationController outer) {
            mOuter = outer;
        }

        @Override
        public void onAnimationFinished() throws RemoteException {
            ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-onAnimationFinished(): mOuter=%s", mOuter);
            final long token = Binder.clearCallingIdentity();
            try {
                if (mOuter != null) {
                    mOuter.onAnimationFinished();

                    // In case the client holds on to the finish callback, make sure we don't leak
                    // RemoteAnimationController which in turn would leak the runner on the client.
                    mOuter = null;
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        /**
         * Marks this callback as not be used anymore by releasing the reference to the outer class
         * to prevent memory leak.
         */
        void release() {
            ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-release(): mOuter=%s", mOuter);
            mOuter = null;
        }
    };

这段代码的关键就是调用mOuter.onAnimationFinished();

onAnimationFinished方法的实现

private void onAnimationFinished() {
        ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d",
                mPendingAnimations.size());
        //移除超时回调
        mHandler.removeCallbacks(mTimeoutRunnable);
        synchronized (mService.mGlobalLock) {
            //解除绑定IRemoteAnimationRunner
            unlinkToDeathOfRunner();
            //释放绑定的IRemoteAnimationFinishedCallback
            releaseFinishedCallback();
            //开启事务
            mService.openSurfaceTransaction();
            try {
                ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
                        "onAnimationFinished(): Notify animation finished:");
                //app类型动画结束时回调
                //调用桌面和启动应用的动画结束时回调
                for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
                    final RemoteAnimationRecord adapters = mPendingAnimations.get(i);
                    if (adapters.mAdapter != null) {
                        adapters.mAdapter.mCapturedFinishCallback
                                .onAnimationFinished(adapters.mAdapter.mAnimationType,
                                        adapters.mAdapter);
                    }
                    if (adapters.mThumbnailAdapter != null) {
                        adapters.mThumbnailAdapter.mCapturedFinishCallback
                                .onAnimationFinished(adapters.mThumbnailAdapter.mAnimationType,
                                        adapters.mThumbnailAdapter);
                    }
                    mPendingAnimations.remove(i);
                    ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tcontainer=%s",
                            adapters.mWindowContainer);
                }

                //壁纸类型动画结束时回调
                for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
                    final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i);
                    adapter.getLeashFinishedCallback().onAnimationFinished(
                            adapter.getLastAnimationType(), adapter);
                    mPendingWallpaperAnimations.remove(i);
                    ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken());
                }

                //非App类型动画结束时回调
                for (int i = mPendingNonAppAnimations.size() - 1; i >= 0; i--) {
                    final NonAppWindowAnimationAdapter adapter = mPendingNonAppAnimations.get(i);
                    adapter.getLeashFinishedCallback().onAnimationFinished(
                            adapter.getLastAnimationType(), adapter);
                    mPendingNonAppAnimations.remove(i);
                    ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tnonApp=%s",
                            adapter.getWindowContainer());
                }
            } catch (Exception e) {
                Slog.e(TAG, "Failed to finish remote animation", e);
                throw e;
            } finally {
                mService.closeSurfaceTransaction("RemoteAnimationController#finished");
            }
            // Reset input for all activities when the remote animation is finished.
            final Consumer<ActivityRecord> updateActivities =
                    activity -> activity.setDropInputForAnimation(false);
            mDisplayContent.forAllActivities(updateActivities);
        }
        setRunningRemoteAnimation(false);
        ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
    }

我们这里主要关注的是App类型的动画结束流程,这里通过循环,把桌面和启动的应用动画结束时流程逐个调用。这个循环是反向遍历,因此先走的是桌面动画结束时的回调。

adapters.mAdapter.mCapturedFinishCallback
		  .onAnimationFinished(adapters.mAdapter.mAnimationType,
		          adapters.mAdapter);

mCapturedFinishCallback是RemoteAnimationAdapterWrapper对象,它其实就是SurfaceAnimator.getFinishedCallback方法。

在创建动画leash的流程中,SurfaceAnimator.startAnimation方法中有调用mAnimation.startAnimation(mLeash, t, type, mInnerAnimationFinishedCallback);,这里把mInnerAnimationFinishedCallback赋值给了RemoteAnimationAdapterWrapper的mCapturedFinishCallbackmInnerAnimationFinishedCallback在SurfaceAnimator的构造方法初始化的值是getFinishedCallback(staticAnimationFinishedCallback),即动画完成时的回调mCapturedFinishCallback对应的就是getFinishedCallback(staticAnimationFinishedCallback)。

所以这里mCapturedFinishCallback.onAnimationFinished调用的,实际是调用就是SurfaceAnimator.getFinishedCallback中匿名的(type, anim) -> {......}

回调处理动画完成的逻辑

这里的流程与本地动画流程相似
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java

    private OnAnimationFinishedCallback getFinishedCallback(
            @Nullable OnAnimationFinishedCallback staticAnimationFinishedCallback) {
        return (type, anim) -> {
            synchronized (mService.mGlobalLock) {
                //移除AnimationAdapter对应的SurfaceAnimator,并将这个SurfaceAnimator返回给target
                //mAnimationTransferMap属于启动窗口的动画场景,这里我们不涉及
                final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim);
                if (target != null) {
                    //递归调用onAnimationFinished(type, anim),即return (type, anim) -> {......}
                    //直到所有的SurfaceAnimator移除完
                    target.mInnerAnimationFinishedCallback.onAnimationFinished(type, anim);
                    return;
                }

                //检查动画是否已被新动画替换,如果当前动画 (anim) 不等于之前存储的动画 (mAnimation),则不执行后续操作
                if (anim != mAnimation) {
                    return;
                }
                //定义一个名为 resetAndInvokeFinish 的 Runnable
                final Runnable resetAndInvokeFinish = () -> {
                    // We need to check again if the animation has been replaced with a new
                    // animation because the animatable may defer to finish.
                    //再次检查动画是否已被新动画替换,因为可设置动画可能会延迟到完成。
                    if (anim != mAnimation) {
                        return;
                    }
                    //mSurfaceAnimationFinishedCallback是在WindowContainer.startAnimation中赋值的
                    //其传递值为null,最终SurfaceAnimator.startAnimation赋值给mSurfaceAnimationFinishedCallback
                    final OnAnimationFinishedCallback animationFinishCallback =
                            mSurfaceAnimationFinishedCallback;
                    //重置与动画相关的状态
                    reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);
                    //WindowContainer构造方法中给SurfaceAnimator构造方法传递了staticAnimationFinishedCallback
                    if (staticAnimationFinishedCallback != null) {
                        //当一个Surface上的动画结束或取消且不重新启动时,这个回调将被执行。
                        //这是一个静态回调,它对通过这个 SurfaceAnimator 启动的所有动画都有效。
                        //回调WindowContainer.onAnimationFinished方法
                        staticAnimationFinishedCallback.onAnimationFinished(type, anim);
                    }
                    //mSurfaceAnimationFinishedCallback的值为null,因此animationFinishCallback的值为null
                    if (animationFinishCallback != null) {
                        //当一个Surface上的动画结束或取消且不重新启动时,这个回调将被执行。
                        //这个回调是每个动画(即每个 AnimationAdapter)特有的。
                        //如果在WindowContainer.startAnimation方法中有赋值,
                        //则回调WindowContainer.onAnimationFinished方法
                        animationFinishCallback.onAnimationFinished(type, anim);
                    }
                };
                // If both the Animatable and AnimationAdapter requests to be deferred, only the
                // first one will be called.
                //如果 mAnimatable 或动画本身请求延迟动画完成,并且它们都没有被延迟,
                //那么直接执行 resetAndInvokeFinish.run()。否则,延迟执行。
                if (!(mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)
                        || anim.shouldDeferAnimationFinish(resetAndInvokeFinish))) {
                    resetAndInvokeFinish.run();
                }
                //设置动画完成标志,将 mAnimationFinished 设置为 true
                mAnimationFinished = true;
            }
        };
    }

这个方法主要做了这几件事:

  1. 通过递归的方式移除所有AnimationAdapter对应的SurfaceAnimator
    其中的mAnimationTransferMap在启动窗口流程中,ActivityRecord.addStartingWindow中有调用transferStartingWindow方法,逐步调用到SurfaceAnimator.transferAnimation中进行添加mService.mAnimationTransferMap.put(mAnimation, this);,这里我们不涉及,因此target的值为null
  2. 使用shouldDeferAnimationFinish方法(默认返回false)用来判断是否需要延迟完成动画
  3. 执行resetAndInvokeFinish.run(),调用reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);重置动画相关状态
  4. 最后调用回调通过staticAnimationFinishedCallback.onAnimationFinished(type, anim);,调用WindowContainer.onAnimationFinished方法处理和响应动画完成的逻辑

重置动画相关状态并移除leash

reset(mAnimatable.getSyncTransaction(), true /* destroyLeash */);
代码路径:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimator.java

    private void reset(Transaction t, boolean destroyLeash) {
        //移除AnimationAdapter对应的SurfaceAnimator
        mService.mAnimationTransferMap.remove(mAnimation);
        mAnimation = null;
        mSurfaceAnimationFinishedCallback = null;
        //动画类型置为空
        mAnimationType = ANIMATION_TYPE_NONE;
        //屏幕冻结时的快照
        final SurfaceFreezer.Snapshot snapshot = mSnapshot;
        mSnapshot = null;
        if (snapshot != null) {
            // Reset the mSnapshot reference before calling the callback to prevent circular reset.
            //如果有屏幕冻结时的快照,取消该动画。
            //最终会调用到SurfaceAnimationRunner.onAnimationCancelled
            snapshot.cancelAnimation(t, !destroyLeash);
        }
        if (mLeash == null) {
            return;
        }
        //使用leash存储动画图层mLeash
        SurfaceControl leash = mLeash;
        //把动画图层置为空
        mLeash = null;
        //移除leash
        final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
        //将mAnimationFinished设置为false
        mAnimationFinished = false;
        if (scheduleAnim) {
            //leash成功移除后,在WMS中会通过WindowAnimator调度动画,协调各个窗口
            mService.scheduleAnimationLocked();
        }
    }
移除leash

调用removeLeash方法移除leash
final boolean scheduleAnim = removeLeash(t, mAnimatable, leash, destroyLeash);
传递的mAnimatable表示当前窗口,leash就是动画图层,destroyLeash在前面getFinishedCallback流程中传递的值为true

    static boolean removeLeash(Transaction t, Animatable animatable, @NonNull SurfaceControl leash,
                               boolean destroy) {
        /* log add start*/
        Slog.i("WindowManager:","removeLeash leash = " + leash , new Exception());
        /* log add end*/
        //scheduleAnim一个标志位,初始值为false
        //为true时,走前面reset方法中的mService.scheduleAnimationLocked()流程
        boolean scheduleAnim = false;
        //获取当前窗口的SurfaceControl
        final SurfaceControl surface = animatable.getSurfaceControl();
        //获取当前窗口的父窗口的SurfaceControl
        final SurfaceControl parent = animatable.getParentSurfaceControl();
        //获取动画图层
        final SurfaceControl curAnimationLeash = animatable.getAnimationLeash();
        // If the surface was destroyed or the leash is invalid, we don't care to reparent it back.
        // Note that we also set this variable to true even if the parent isn't valid anymore, in
        // order to ensure onAnimationLeashLost still gets called in this case.
        // If the animation leash is set, and it is different from the removing leash, it means the
        // surface now has a new animation surface. We don't want to reparent for that.
        //1.surface不为空
        //2.curAnimationLeash不为空,且curAnimationLeash等于leash
        //因此reparent值为true
        final boolean reparent = surface != null && (curAnimationLeash == null
                || curAnimationLeash.equals(leash));
        if (reparent) {
            ProtoLog.i(WM_DEBUG_ANIM, "Reparenting to original parent: %s for %s",
                    parent, animatable);
            // We shouldn't really need these isValid checks but we do
            // b/130364451
            //判断当前窗口的surface是否有效,以及该窗口的父窗口的图层不为空且有效
            if (surface.isValid() && parent != null && parent.isValid()) {
                //把当前窗口图层和其父窗口的图层重新建立父子关系
                t.reparent(surface, parent);
                //scheduleAnim置为true
                scheduleAnim = true;
            }
        }
        //destroy传递过来值为true
        if (destroy) {
            //移除图层
            t.remove(leash);
            //scheduleAnim置为true
            scheduleAnim = true;
        }

        if (reparent) {
            // Make sure to inform the animatable after the surface was reparented (or reparent
            // wasn't possible, but we still need to invoke the callback)
            //1.移除和leash相关联的窗口和surface(这个只在前面requiresEdgeExtension为true时逻辑中有涉及)
            //2.调整surface
            animatable.onAnimationLeashLost(t);
            //scheduleAnim置为true
            scheduleAnim = true;
        }
        return scheduleAnim;
    }
  • 获取当前窗口的图层
    final SurfaceControl surface = animatable.getSurfaceControl();
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

        /**
         * @return The SurfaceControl for this container.
         *         The SurfaceControl must be valid if non-null.
         */
        @Override
        public SurfaceControl getSurfaceControl() {
            return mSurfaceControl;
        }
    

    直接返回一个SurfaceControl。

  • 获取当前窗口父窗口的图层
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

        /*
         * @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();
        }
        
       @Override
       final protected WindowContainer getParent() {
           return mParent;
       }
    

    先获取当前窗口的父窗口,在获取父窗口的SurfaceControl。

  • 获取动画图层
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    @Override
        public SurfaceControl getAnimationLeash() {
            return mAnimationLeash;
        }
    

    mAnimationLeash是前面SurfaceAnimator的startAnimation方法中的mAnimatable.onAnimationLeashCreated(t, mLeash);,把mLeash赋值给了mAnimationLeash,因此这个方法获取的是动画图层。

  • 当前窗口图层和其父窗口的图层重新建立父子关系
    t.reparent(surface, parent);
    桌面的SurfaceControl重新认DefaultTaskDsiplayArea的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;
            }
    

    前面说过reparent方法中通过mReparentedSurfaces这个ArrayMap临时存储父子关系,key值存储SurfaceControl对象,value为其父SurfaceControl对象(当前窗口的父窗口的SurfaceControl,即DefaultTaskDsiplayArea的SurfaceControl)
    此时leash还没有被释放,DefaultTaskDsiplayArea的SurfaceControl有两个儿子SurfaceControl,(以桌面为例)关系如下图所示:
    在这里插入图片描述

    此时leash逐渐发现不对劲,但是假装不知道
    假如我们后面不执行移除leash图层的操作,那么这个图层一直会保持这个状态挂在DefaultTaskDsiplayArea上和桌面Task共享父亲。

  • 移除动画图层
    t.remove(leash);

            /**
             * Equivalent to reparent with a null parent, in that it removes
             * the SurfaceControl from the scene, but it also releases
             * the local resources (by calling {@link SurfaceControl#release})
             * after this method returns, {@link SurfaceControl#isValid} will return
             * false for the argument.
             *
             * @param sc The surface to remove and release.
             * @return This transaction
             * @hide
             */
            @NonNull
            public Transaction remove(@NonNull SurfaceControl sc) {
                reparent(sc, null);
                sc.release();
                return this;
            }
    

    同样调用了reparent方法,先把需要remove的图层的父图层置空,然后释放。
    过程如下所示:
    在这里插入图片描述
    leash:原来我才是多余的那个,悠悠苍天,何薄于我!

  • 移除和leash相关联的窗口和surface并调整surface
    animatable.onAnimationLeashLost(t);
    代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

        @Override
        public void onAnimationLeashLost(Transaction t) {
            mLastLayer = -1;
            //调用mWmService中的mSurfaceAnimationRunner对象的onAnimationLeashLost方法
            //用于移除和leash相关联的窗口,这个只在前面requiresEdgeExtension为true时逻辑中有涉及
            mWmService.mSurfaceAnimationRunner.onAnimationLeashLost(mAnimationLeash, t);
            //mAnimationLeash置为空
            mAnimationLeash = null;
            mNeedsZBoost = false;
            //调整其所有child的z-order
            reassignLayer(t);
            //更新Surface位置
            updateSurfacePosition(t);
        }
    

    其中mWmService.mSurfaceAnimationRunner.onAnimationLeashLost(mAnimationLeash, t);mAnimationLeash前面说过就是动画图层。这个只在前面SurfaceAnimationRunner的startAnimation方法中requiresEdgeExtensiontrue时逻辑中有涉及,其为true时才会操作mEdgeExtensions这个ArrayList,这里不讨论。

协调动画显示

在SurfaceAnimator.reset()方法最后调用了mService.scheduleAnimationLocked();
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

    void scheduleAnimationLocked() {
        mAnimator.scheduleAnimation();
    }

    void scheduleAnimation() {
        if (!mAnimationFrameCallbackScheduled) {
            //mAnimationFrameCallbackScheduled 设置为 true,表示动画帧回调已经安排
            mAnimationFrameCallbackScheduled = true;
            //每一帧被绘制时,回调mAnimationFrameCallback
            mChoreographer.postFrameCallback(mAnimationFrameCallback);
        }
    }

这个方法的主要作用是确保动画帧回调被正确地安排,以便在每一帧绘制时执行,可以确保动画在每一帧都被调用,从而平滑地更新和显示动画。

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

回到SurfaceAnimator.getFinishedCallback中匿名的onAnimationFinished方法中有调用staticAnimationFinishedCallback.onAnimationFinished(type, anim);处理和响应动画完成的逻辑。

这里的staticAnimationFinishedCallback也是在SurfaceAnimator构造方法中初始化的

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

在WindowContainer构造方法中初始化mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);,因此staticAnimationFinishedCallback.onAnimationFinished对应的就是WindowContainer.onAnimationFinished方法

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

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

这个里面又调用了另一个doAnimationFinished(type, anim);

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

		

WindowContainer.cancelAnimation方法中调用的doAnimationFinished也是这个方法。

我们这里mSurfaceAnimationSources是保存的是需要做动画的ActivityRecord,即桌面ActivityRecord和启动应用的ActivityRecord。
mSurfaceAnimationSources的值是在前面系统侧动画启动流程中WindowContainer.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);
    }

协调动画显示

和前面在SurfaceAnimator.reset()方法最后调用了mService.scheduleAnimationLocked();相似,
这里我们调用的scheduleAnimation();
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    /**
     * Trigger a call to prepareSurfaces from the animation thread, such that pending transactions
     * will be applied.
     */
    void scheduleAnimation() {
        mWmService.scheduleAnimationLocked();
    }

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

    void scheduleAnimationLocked() {
        mAnimator.scheduleAnimation();
    }

最终调用到了WindowAnimator.scheduleAnimation()
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java

    void scheduleAnimation() {
        if (!mAnimationFrameCallbackScheduled) {
            //mAnimationFrameCallbackScheduled 设置为 true,表示动画帧回调已经安排
            mAnimationFrameCallbackScheduled = true;
            //每一帧被绘制时,回调mAnimationFrameCallback
            mChoreographer.postFrameCallback(mAnimationFrameCallback);
        }
    }

这个方法的主要作用是确保动画帧回调被正确地安排,以便在每一帧绘制时执行,可以确保动画在每一帧都被调用,从而平滑地更新和显示动画。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大龙谈智能内容开通视频号啦

大家好&#xff0c;大龙谈只能内容开通视频号了&#xff0c;欢迎大家扫码关注&#xff1a;

Android修行手册-集成Python开发环境

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

计算机网络——22TCP拥塞

TCP拥塞 TCP拥塞控制机制 端到端的拥塞控制机制 路由器不向主机有关拥塞的反馈信息 路由器的负担较轻符合网络核心简单的TCP/IP架构原则 端系统根据自身得到的信息&#xff0c;判断是否发生拥塞&#xff0c;从而采取动作 拥塞控制的几个问题 如何检测拥塞 轻微拥塞拥塞 控…

【Maven】Maven 基础教程(三):build、profile

《Maven 基础教程》系列&#xff0c;包含以下 3 篇文章&#xff1a; Maven 基础教程&#xff08;一&#xff09;&#xff1a;基础介绍、开发环境配置Maven 基础教程&#xff08;二&#xff09;&#xff1a;Maven 的使用Maven 基础教程&#xff08;三&#xff09;&#xff1a;b…

Vue+SpringBoot打造个人保险管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 登录注册模块2.2 保险档案模块2.3 保险订单模块2.4 保险理赔模块 三、系统展示四、核心代码4.1 查询保险产品4.2 新增保险预定4.3 订单支付4.4 新增理赔单4.5 查询保险理赔 五、免责说明 一、摘要 1.1 项目介绍 基于J…

计算以e为底1+x的自然对数 即ln(1+x) math.log1p(x)

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算以e为底 1x的自然对数 即ln(1x) math.log1p(x) [太阳]选择题 请问执行以下程序的运行结果是&#xff1a; import math print("【执行】math.log1p(0)") print (math.log1p(0)) …

服务端向客户端推送数据的实现方案

在日常的开发中&#xff0c;我们经常能碰见服务端需要主动推送给客户端数据的业务场景&#xff0c;比如数据大屏的实时数据&#xff0c;比如消息中心的未读消息&#xff0c;比如聊天功能等等。 本文主要介绍SSE的使用场景和如何使用SSE。 服务端向客户端推送数据的实现方案有哪…

Vue路由模式

聚沙成塔每天进步一点点 本文内容 ⭐ 专栏简介路由模式1. Hash 模式2. History 模式选择路由模式的考虑因素 ⭐ 写在最后 ⭐ 专栏简介 Vue学习之旅的奇妙世界 欢迎大家来到 Vue 技能树参考资料专栏&#xff01;创建这个专栏的初衷是为了帮助大家更好地应对 Vue.js 技能树的学习…

Node.js中的数据加密和安全传输

在当今日益数字化的世界中&#xff0c;数据安全一直是一个备受关注的话题。Node.js作为一种流行的后端开发技术&#xff0c;其在数据加密和安全传输方面的应用也备受关注。本篇博客将深入探讨Node.js中的数据加密和安全传输相关内容&#xff0c;并为读者提供一些示例代码&#…

vue实现水印功能

目录 一、应用场景 二、实现原理 三、详细开发 1.水印的实现方式 2.防止用户通过控制台修改样式去除水印效果&#xff08;可跳过&#xff0c;有弊端&#xff09; 3.水印的使用 &#xff08;1&#xff09;单页面/全局使用 &#xff08;2&#xff09;全局使用个别页面去掉…

【投稿优惠|快速见刊】2024年图像,机器学习和人工智能国际会议(ICIMLAI 2024)

【投稿优惠|快速见刊】2024年图像&#xff0c;机器学习和人工智能国际会议&#xff08;ICIMLAI 2024&#xff09; 重要信息 会议官网&#xff1a;http://www.icimlai.com会议地址&#xff1a;深圳召开日期&#xff1a;2024.03.30截稿日期&#xff1a;2024.03.20 &#xff08;先…

音视频开发之旅(72)- AI数字人-照片说话之SadTalker

目录 1.效果展示 2.SadTalker原理学习 3.SadTalker代码流程分析 4.性能优化 5.参考资料 AI数字人目前做的最好的无疑是heygen&#xff0c;但是费用也是很贵&#xff0c;也有一些其他的商业应用&#xff0c;比如&#xff1a;微软小冰、腾讯智影、万兴播爆和硅基智能等。 而…

如何确保JDK版本与操作系统架构匹配?

1. 序言 最近的工作中&#xff0c;需要升级JDK版本到17.0.7&#xff0c;以解决一个JDK bug&#xff1a;JDK-8299626该bug的core dump关键字如下&#xff1a;SIGSEGV in PhaseIdealLoop::build_loop_late_post_work公司JDK团队提供的、包含JDK的基础镜像&#xff0c;有aarch64和…

基于C#开发OPC DA客户端——搭建KEPServerEX服务

简介 OPC DA (OLE for Process Control Data Access) 是一种工业自动化领域中的通信协议标准&#xff0c;它定义了应用程序如何访问由OPC服务器提供的过程控制数据。OPC DA标准允许软件应用程序&#xff08;客户端&#xff09;从OPC服务器读取实时数据或向服务器写入数据&…

elment-ui table表格排序后 清除排序箭头/恢复默认排序 的高亮样式

问题描述&#xff1a; 1.默认排序是按照名称升序排列&#xff08;图一&#xff09; 2.在选择了筛选项以及其他排序方式之后&#xff0c;箭头高亮是这样的&#xff08;图二&#xff09; 3.当我点击清空按钮后&#xff0c;类型清空了&#xff0c;并且传给后端的排序方式是名称/升…

数据库管理-第157期 Oracle Vector DB AI-08(20240301)

数据库管理157期 2024-03-01 数据库管理-第157期 Oracle Vector DB & AI-08&#xff08;20240301&#xff09;1 创建示例向量2 查找最近向量3 基于向量簇组的最近向量查询总结 数据库管理-第157期 Oracle Vector DB & AI-08&#xff08;20240301&#xff09; 作者&…

蓝桥杯-单片机组基础5——外部中断与LED的控制(附小蜜蜂课程代码)

蓝桥杯单片机组备赛指南请查看这篇文章&#xff1a;戳此跳转蓝桥杯备赛指南文章 本文章针对蓝桥杯-单片机组比赛开发板所写&#xff0c;代码可直接在比赛开发板上使用。 型号&#xff1a;国信天长4T开发板&#xff08;绿板&#xff09;&#xff0c;芯片&#xff1a;IAP15F2K6…

二叉搜索树在线OJ题讲解

二叉树创建字符串 我们首先进行题目的解读&#xff1a; 大概意思就是用&#xff08;&#xff09;把每个节点的值给括起来&#xff0c;然后再经过一系列的省略的来得到最后的结果 大家仔细观察题目给出的列子就可以发现&#xff0c;其实这个题目可以大致分为三种情况&#xff1…

git安装与使用4.3

一、git的安装 1、下载git包 下载git包url&#xff1a;https://git-scm.com/download/win 下载包分为&#xff1a;64位和32位 2、点击安装包 2、选择安装路径 3、 点击下一步 4、点击next 5、点击next 6、点击next 7、 8、 9、 10、 11、 12、在桌面空白处&#xff0c;右键…

服务器上部署WEb服务方法

部署Web服务在服务器上是一个比较复杂的过程。这不仅仅涉及到配置环境、选择软件和设置端口&#xff0c;更有众多其它因素需要考虑。以下是在服务器上部署WEb服务的步骤&#xff1a; 1. 选择服务器&#xff1a;根据项目规模和预期访问量&#xff0c;选择合适的服务器类型和配置…
最新文章