Android 12系统源码_窗口动画(二)应用程序间的切换动画

前言

在 Android 系统中,系统主要是通过AppTransitionController和AppTransition这两个类是用于管理应用程序间切换动画的重要类。

一、简介

1、AppTransitionController

AppTransitionController 是一个系统级别的类,用于管理应用程序间切换时的动画效果。其作用包括:

  • 动画类型管理:控制不同应用切换时的动画类型,如窗口动画、应用切换动画等。
  • 动画资源管理:管理和加载与切换动画相关的资源,如动画文件、持续时间等。
  • 调度动画:决定何时开始和结束应用程序切换的动画。

2、AppTransition

AppTransition 是 AppTransitionController 的一部分,负责实际执行应用切换动画。主要功能包括:

  • 动画执行:根据 AppTransitionController 的设置,执行应用程序间的切换动画。
  • 动画参数设置:配置和管理动画的参数,例如动画类型、持续时间、插值器等。
  • 状态监控:监控动画执行的状态,以便在动画完成或被取消时进行适当的处理。

3、使用场景和重要性

这两个类在 Android 系统中的重要性体现在以下几个方面:

  • 用户体验:应用程序切换时的流畅动画可以显著提升用户体验,这些动画通过 AppTransitionController 和 AppTransition 进行管理和优化。
  • 系统一致性:Android 系统通过这些类确保应用程序切换时的动画表现一致性,使整个系统看起来更加统一。
  • 定制性:开发者可以通过这些类定制应用切换时的动画效果,以满足特定应用或设备的需求。

二、对象的创建

1、AppTransition和AppTransitionController实例对象的创建。

AppTransition和AppTransitionController实例对象都是在DisplayContent对象的构造方法中被创建的。

frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

java 复制代码
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
        implements WindowManagerPolicy.DisplayContentInfo {
        
     DisplayContent(Display display, RootWindowContainer root) {
        super(root.mWindowManager);       
        ...代码省略...
        mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
        mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
        mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
        mAppTransitionController = new AppTransitionController(mWmService, this);
        ...代码省略...
     }

}

2、关于DisplayContent对象

我们在Android 12系统源码_窗口管理(七)DisplayContent简介这篇文章有讲过,DisplayContent 用于管理屏幕,一块DisplayContent 对象实例代表一个屏幕设备,这样有多个屏幕的设备就可以创建多个DisplayContent 对象,虽然多数设备只有一个显示屏,但它们同样可以创建多个 DisplayContent 对象,如投屏的时候,可以创建一个虚拟的DisplayContent,DisplayContent会根据窗口的显示位置将窗口进行分组,隶属于同一个DisplayContent的窗口将会显示在同一个屏幕中,每一个DisplayContent都对应一个唯一的ID,在添加窗口时可以通过指定这个ID决定其将被显示在对应的个屏幕中。DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合,而如果我们想让不同DisplayContent的窗口切换动画不同,就可以通过定制该DisplayContent对应的AppTransition和AppTransitionController来实现。

三、AppTransition

来看下AppTransition的相关的源码。

frameworks/base/services/core/java/com/android/server/wm/AppTransition.java

java 复制代码
public class AppTransition implements Dump {

    static final int DEFAULT_APP_TRANSITION_DURATION = 336;//应用默认专场动画的执行时间
    
    private final Context mContext;
    private final WindowManagerService mService;
    private final DisplayContent mDisplayContent;
    
    private final TransitionAnimation mTransitionAnimation;//应用间的切换动画

    private final int mConfigShortAnimTime;
    private final Interpolator mDecelerateInterpolator;
    private final Interpolator mThumbnailFadeInInterpolator;
    private final Interpolator mThumbnailFadeOutInterpolator;
    private final Interpolator mLinearOutSlowInInterpolator;
    private final Interpolator mFastOutLinearInInterpolator;
    private final Interpolator mFastOutSlowInInterpolator;
    
    private final int mClipRevealTranslationY;

    private final boolean mGridLayoutRecentsEnabled;
    private final boolean mLowRamRecentsEnabled;
    
    private final int mDefaultWindowAnimationStyleResId;
    
    private RemoteAnimationController mRemoteAnimationController;

    final Handler mHandler;
    
    AppTransition(Context context, WindowManagerService service, DisplayContent displayContent) {
        mContext = context;
        mService = service;
        mHandler = new Handler(service.mH.getLooper());
        mDisplayContent = displayContent;
        mTransitionAnimation = new TransitionAnimation(context, DEBUG_ANIM, TAG);
        mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                com.android.internal.R.interpolator.linear_out_slow_in);
        mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
                com.android.internal.R.interpolator.fast_out_linear_in);
        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                com.android.internal.R.interpolator.fast_out_slow_in);
        mConfigShortAnimTime = context.getResources().getInteger(
                com.android.internal.R.integer.config_shortAnimTime);
        mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
                com.android.internal.R.interpolator.decelerate_cubic);
        mThumbnailFadeInInterpolator = new Interpolator() {
            @Override
            public float getInterpolation(float input) {
                // Linear response for first fraction, then complete after that.
                if (input < RECENTS_THUMBNAIL_FADEIN_FRACTION) {
                    return 0f;
                }
                float t = (input - RECENTS_THUMBNAIL_FADEIN_FRACTION) /
                        (1f - RECENTS_THUMBNAIL_FADEIN_FRACTION);
                return mFastOutLinearInInterpolator.getInterpolation(t);
            }
        };
        mThumbnailFadeOutInterpolator = new Interpolator() {
            @Override
            public float getInterpolation(float input) {
                // Linear response for first fraction, then complete after that.
                if (input < RECENTS_THUMBNAIL_FADEOUT_FRACTION) {
                    float t = input / RECENTS_THUMBNAIL_FADEOUT_FRACTION;
                    return mLinearOutSlowInInterpolator.getInterpolation(t);
                }
                return 1f;
            }
        };
        mClipRevealTranslationY = (int) (CLIP_REVEAL_TRANSLATION_Y_DP
                * mContext.getResources().getDisplayMetrics().density);
        mGridLayoutRecentsEnabled = SystemProperties.getBoolean("ro.recents.grid", false);
        mLowRamRecentsEnabled = ActivityManager.isLowRamDeviceStatic();

        final TypedArray windowStyle = mContext.getTheme().obtainStyledAttributes(
                com.android.internal.R.styleable.Window);
        mDefaultWindowAnimationStyleResId = windowStyle.getResourceId(
                com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
        windowStyle.recycle();
    }
    
    /**
     *
     * @param lp 窗口参数
     * @param transit 窗口切换动画类型
     * @param enter 是否是进入
     * @param frame 如果是进入动画,则是动画结束后窗口的位置,如果是退出动画,则是动画开始时窗口的位置
     * @return 返回的就是最终的窗口转换动画
     */
    Animation loadAnimation(LayoutParams lp, int transit, boolean enter, int uiMode,
            int orientation, Rect frame, Rect displayFrame, Rect insets,
            @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
            boolean freeform, WindowContainer container) {

        if (mNextAppTransitionOverrideRequested
                && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) {
            mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
        }

        Animation a;
        if (isKeyguardGoingAwayTransitOld(transit) && enter) {
            a = mTransitionAnimation.loadKeyguardExitAnimation(mNextAppTransitionFlags,
                    transit == TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER);
        } else if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
            a = null;
        } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE && !enter) {
            a = mTransitionAnimation.loadKeyguardUnoccludeAnimation();
        } else if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
            a = null;
        } else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_OPEN
                || transit == TRANSIT_OLD_TASK_OPEN
                || transit == TRANSIT_OLD_TASK_TO_FRONT)) {
            a = mTransitionAnimation.loadVoiceActivityOpenAnimation(enter);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a,
                    appTransitionOldToString(transit), enter, Debug.getCallers(3));
        } else if (isVoiceInteraction && (transit == TRANSIT_OLD_ACTIVITY_CLOSE
                || transit == TRANSIT_OLD_TASK_CLOSE
                || transit == TRANSIT_OLD_TASK_TO_BACK)) {
            a = mTransitionAnimation.loadVoiceActivityExitAnimation(enter);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation voice: anim=%s transit=%s isEntrance=%b Callers=%s", a,
                    appTransitionOldToString(transit), enter, Debug.getCallers(3));
        } else if (transit == TRANSIT_OLD_ACTIVITY_RELAUNCH) {
            a = mTransitionAnimation.createRelaunchAnimation(frame, insets,
                    mDefaultNextAppTransitionAnimationSpec != null
                            ? mDefaultNextAppTransitionAnimationSpec.rect : null);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s transit=%s Callers=%s", a,
                    appTransitionOldToString(transit), Debug.getCallers(3));
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
            a = mTransitionAnimation.loadAppTransitionAnimation(mNextAppTransitionPackage,
                    enter ? mNextAppTransitionEnter : mNextAppTransitionExit);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s "
                            + "isEntrance=%b Callers=%s",
                    a, appTransitionOldToString(transit), enter, Debug.getCallers(3));
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) {
            a = mTransitionAnimation.loadAppTransitionAnimation(
                    mNextAppTransitionPackage, mNextAppTransitionInPlace);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM_IN_PLACE "
                            + "transit=%s Callers=%s",
                    a, appTransitionOldToString(transit), Debug.getCallers(3));
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) {
            a = mTransitionAnimation.createClipRevealAnimationLockedCompat(
                    transit, enter, frame, displayFrame,
                    mDefaultNextAppTransitionAnimationSpec != null
                            ? mDefaultNextAppTransitionAnimationSpec.rect : null);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s nextAppTransition=ANIM_CLIP_REVEAL "
                            + "transit=%s Callers=%s",
                    a, appTransitionOldToString(transit), Debug.getCallers(3));
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) {
            a = mTransitionAnimation.createScaleUpAnimationLockedCompat(transit, enter, frame,
                    mDefaultNextAppTransitionAnimationSpec != null
                            ? mDefaultNextAppTransitionAnimationSpec.rect : null);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s nextAppTransition=ANIM_SCALE_UP transit=%s "
                            + "isEntrance=%s Callers=%s",
                    a, appTransitionOldToString(transit), enter, Debug.getCallers(3));
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP ||
                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
            mNextAppTransitionScaleUp =
                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
            final HardwareBuffer thumbnailHeader = getAppTransitionThumbnailHeader(container);
            a = mTransitionAnimation.createThumbnailEnterExitAnimationLockedCompat(enter,
                    mNextAppTransitionScaleUp, frame, transit, thumbnailHeader,
                    mDefaultNextAppTransitionAnimationSpec != null
                            ? mDefaultNextAppTransitionAnimationSpec.rect : null);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b "
                            + "Callers=%s",
                    a,  mNextAppTransitionScaleUp
                            ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN",
                    appTransitionOldToString(transit), enter, Debug.getCallers(3));
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP ||
                mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) {
            mNextAppTransitionScaleUp =
                    (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
            AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
                    container.hashCode());
            a = mTransitionAnimation.createAspectScaledThumbnailEnterExitAnimationLocked(enter,
                    mNextAppTransitionScaleUp, orientation, transit, frame, insets, surfaceInsets,
                    stableInsets, freeform, spec != null ? spec.rect : null,
                    mDefaultNextAppTransitionAnimationSpec != null
                            ? mDefaultNextAppTransitionAnimationSpec.rect : null);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s nextAppTransition=%s transit=%s isEntrance=%b "
                            + "Callers=%s",
                    a, mNextAppTransitionScaleUp
                            ? "ANIM_THUMBNAIL_ASPECT_SCALE_UP"
                        : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN",
                    appTransitionOldToString(transit), enter, Debug.getCallers(3));
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS && enter) {
            a = mTransitionAnimation.loadCrossProfileAppEnterAnimation();
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS: "
                            + "anim=%s transit=%s isEntrance=true Callers=%s",
                    a, appTransitionOldToString(transit), Debug.getCallers(3));
        } else if (isChangeTransitOld(transit)) {
            // In the absence of a specific adapter, we just want to keep everything stationary.
            a = new AlphaAnimation(1.f, 1.f);
            a.setDuration(WindowChangeAnimationSpec.ANIMATION_DURATION);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s",
                    a, appTransitionOldToString(transit), enter, Debug.getCallers(3));
        } else {
            int animAttr = 0;
            switch (transit) {
                case TRANSIT_OLD_ACTIVITY_OPEN:
                case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN:
                    animAttr = enter
                            ? WindowAnimation_activityOpenEnterAnimation
                            : WindowAnimation_activityOpenExitAnimation;
                    break;
                case TRANSIT_OLD_ACTIVITY_CLOSE:
                case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE:
                    animAttr = enter
                            ? WindowAnimation_activityCloseEnterAnimation
                            : WindowAnimation_activityCloseExitAnimation;
                    break;
                case TRANSIT_OLD_TASK_OPEN:
                    animAttr = enter
                            ? WindowAnimation_taskOpenEnterAnimation
                            : WindowAnimation_taskOpenExitAnimation;
                    break;
                case TRANSIT_OLD_TASK_CLOSE:
                    animAttr = enter
                            ? WindowAnimation_taskCloseEnterAnimation
                            : WindowAnimation_taskCloseExitAnimation;
                    break;
                case TRANSIT_OLD_TASK_TO_FRONT:
                    animAttr = enter
                            ? WindowAnimation_taskToFrontEnterAnimation
                            : WindowAnimation_taskToFrontExitAnimation;
                    break;
                case TRANSIT_OLD_TASK_TO_BACK:
                    animAttr = enter
                            ? WindowAnimation_taskToBackEnterAnimation
                            : WindowAnimation_taskToBackExitAnimation;
                    break;
                case TRANSIT_OLD_WALLPAPER_OPEN:
                    animAttr = enter
                            ? WindowAnimation_wallpaperOpenEnterAnimation
                            : WindowAnimation_wallpaperOpenExitAnimation;
                    break;
                case TRANSIT_OLD_WALLPAPER_CLOSE:
                    animAttr = enter
                            ? WindowAnimation_wallpaperCloseEnterAnimation
                            : WindowAnimation_wallpaperCloseExitAnimation;
                    break;
                case TRANSIT_OLD_WALLPAPER_INTRA_OPEN:
                    animAttr = enter
                            ? WindowAnimation_wallpaperIntraOpenEnterAnimation
                            : WindowAnimation_wallpaperIntraOpenExitAnimation;
                    break;
                case TRANSIT_OLD_WALLPAPER_INTRA_CLOSE:
                    animAttr = enter
                            ? WindowAnimation_wallpaperIntraCloseEnterAnimation
                            : WindowAnimation_wallpaperIntraCloseExitAnimation;
                    break;
                case TRANSIT_OLD_TASK_OPEN_BEHIND:
                    animAttr = enter
                            ? WindowAnimation_launchTaskBehindSourceAnimation
                            : WindowAnimation_launchTaskBehindTargetAnimation;
                    break;
                // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
                //  need new TaskFragment transition.
                case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
                    animAttr = enter
                            ? WindowAnimation_activityOpenEnterAnimation
                            : WindowAnimation_activityOpenExitAnimation;
                    break;
                // TODO(b/189386466): Use activity transition as the fallback. Investigate if we
                //  need new TaskFragment transition.
                case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
                    animAttr = enter
                            ? WindowAnimation_activityCloseEnterAnimation
                            : WindowAnimation_activityCloseExitAnimation;
                    break;
            }
            //动画
            a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b "
                            + "Callers=%s",
                    a, animAttr, appTransitionOldToString(transit), enter,
                    Debug.getCallers(3));
        }
        setAppTransitionFinishedCallbackIfNeeded(a);
        return a;
    }
    
    /**
     *
     * @param lp 窗口参数
     * @param animAttr 动画资源id
     * @param transit 窗口切换动画类型
     * @return Animation动画
     */
    @Nullable
    Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
        return mTransitionAnimation.loadAnimationAttr(lp, animAttr, transit);
    }

}

frameworks/base/core/java/com/android/internal/policy/TransitionAnimation.java

java 复制代码
public class TransitionAnimation {
    @Nullable
    public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
        int resId = Resources.ID_NULL;
        Context context = mContext;
        if (animAttr >= 0) {
            AttributeCache.Entry ent = getCachedAnimations(lp);
            if (ent != null) {
                context = ent.context;
                resId = ent.array.getResourceId(animAttr, 0);
            }
        }
        resId = updateToTranslucentAnimIfNeeded(resId, transit);
        if (ResourceId.isValid(resId)) {
            return loadAnimationSafely(context, resId, mTag);
        }
        return null;
    }
    
    private static int updateToTranslucentAnimIfNeeded(int anim, @TransitionOldType int transit) {
        if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN
                && anim == R.anim.activity_open_enter) {
            return R.anim.activity_translucent_open_enter;
        }
        if (transit == TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE
                && anim == R.anim.activity_close_exit) {
            return R.anim.activity_translucent_close_exit;
        }
        return anim;
    }
 }
相关推荐
闲暇部落1 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX3 小时前
Android 分区相关介绍
android
大白要努力!4 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee4 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood5 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-8 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen10 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年17 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿20 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神21 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri