通过Theme设置的动画

支持的动画属性

在AndroidManifest.xml中通过设置style的形式指定动画资源,比如有:activityOpenEnterAnimation

framework中定义动画的属性文件为:base/core/res/res/values/attrs.xml 其中 <declare-styleable name="WindowAnimation">

默认动画

对应的默认动画资源文件:base/core/res/res/values/styles.xml 其中 <style name="Animation.Activity">, 完整定义:

    <!-- Standard animations for a full-screen window or activity. -->
    <style name="Animation.Activity">
        <item name="activityOpenEnterAnimation">@anim/activity_open_enter</item>
        <item name="activityOpenExitAnimation">@anim/activity_open_exit</item>
        <item name="activityCloseEnterAnimation">@anim/activity_close_enter</item>
        <item name="activityCloseExitAnimation">@anim/activity_close_exit</item>
        <item name="dreamActivityCloseExitAnimation">@anim/dream_activity_close_exit</item>
        <item name="dreamActivityOpenEnterAnimation">@anim/dream_activity_open_enter</item>
        <item name="dreamActivityOpenExitAnimation">@anim/dream_activity_open_exit</item>
        <item name="taskOpenEnterAnimation">@anim/task_open_enter</item>
        <item name="taskOpenExitAnimation">@anim/task_open_exit</item>
        <item name="launchTaskBehindTargetAnimation">@anim/launch_task_behind_target</item>
        <item name="launchTaskBehindSourceAnimation">@anim/launch_task_behind_source</item>
        <item name="taskCloseEnterAnimation">@anim/task_close_enter</item>
        <item name="taskCloseExitAnimation">@anim/task_close_exit</item>
        <item name="taskToFrontEnterAnimation">@anim/task_open_enter</item>
        <item name="taskToFrontExitAnimation">@anim/task_open_exit</item>
        <item name="taskToBackEnterAnimation">@anim/task_close_enter</item>
        <item name="taskToBackExitAnimation">@anim/task_close_exit</item>
        <item name="wallpaperOpenEnterAnimation">@anim/wallpaper_open_enter</item>
        <item name="wallpaperOpenExitAnimation">@anim/wallpaper_open_exit</item>
        <item name="wallpaperCloseEnterAnimation">@anim/wallpaper_close_enter</item>
        <item name="wallpaperCloseExitAnimation">@anim/wallpaper_close_exit</item>
        <item name="wallpaperIntraOpenEnterAnimation">@anim/wallpaper_intra_open_enter</item>
        <item name="wallpaperIntraOpenExitAnimation">@anim/wallpaper_intra_open_exit</item>
        <item name="wallpaperIntraCloseEnterAnimation">@anim/wallpaper_intra_close_enter</item>
        <item name="wallpaperIntraCloseExitAnimation">@anim/wallpaper_intra_close_exit</item>
        <item name="fragmentOpenEnterAnimation">@animator/fragment_open_enter</item>
        <item name="fragmentOpenExitAnimation">@animator/fragment_open_exit</item>
        <item name="fragmentCloseEnterAnimation">@animator/fragment_close_enter</item>
        <item name="fragmentCloseExitAnimation">@animator/fragment_close_exit</item>
        <item name="fragmentFadeEnterAnimation">@animator/fragment_fade_enter</item>
        <item name="fragmentFadeExitAnimation">@animator/fragment_fade_exit</item>
    </style>

每种动画类型都定义了默认的动画资源

Overlay

厂商一般都会通过定义overlay apk来覆盖默认的动画资源

动画资源的加载

只有activity动画没有被override或者禁用的时候,framework才会加载app自定义的动画,动画的加载位于 base/services/core/java/com/android/server/wm/AppTransition.java loadAnimation函数

加载条件

    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) {

        //1
        final boolean canCustomizeAppTransition = container.canCustomizeAppTransition();

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

         //2
         if(...) {
          ...
         } else {
            //3
            int animAttr = mapOpenCloseTransitTypes(transit, enter);
            //4
            a = animAttr == 0 ? null : (canCustomizeAppTransition
                ? loadAnimationAttr(lp, animAttr, transit)
                : mTransitionAnimation.loadDefaultAnimationAttr(animAttr, transit));

            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b "
                            + " canCustomizeAppTransition=%b Callers=%s",
                    a, animAttr, appTransitionOldToString(transit), enter,
                    canCustomizeAppTransition, Debug.getCallers(3));
        }
canCustomizeAppTransition

关注点1:是否允许加载自定义动画

WindowContainer继承关系
classDiagram WindowContainer <|-- WindowToken WindowContainer: +canCustomizeAppTransition() = false WindowToken <|-- ActivityRecord ActivityRecord: +canCustomizeAppTransition() = true WindowContainer <|-- TaskFragment TaskFragment: +canCustomizeAppTransition() = mIsEmbedded || matchParentBounds TaskFragment <|-- Task

WindowContainer定义为false

ActivityRecord定义为true

TaskFragment 条件为

    @Override
    boolean canCustomizeAppTransition() {
        return isEmbedded() && matchParentBounds();
    }

当一个Activity作为任务栈的根Activity时, 在loadAnimation时发现传入的WindowContainer是Task, 根据上面的继承关系源自于TaskFragment.

全屏的Activity matchParentBounds() 总是为true, 接下来跟踪下 isEmbedded(), 也就是isEmbedded变量的来源

    private Task(...) {
        super(atmService, null /* fragmentToken */, _createdByOrganizer, false /* isEmbedded */);
    }

从源码发现在Task创建的时候传入false

结论:只要是任务栈里边的根Activity,在加载动画资源的时候不允许加载自定义动画

关注点2:这里会对一些override动画类型做判断,或者锁屏,语音窗口的特殊动画

关注点4:此处会检测canCustomizeAppTransition,如果不允许自定义动画,就会加载系统定义的默认动画

资源映射

mapOpenCloseTransitTypes

关注点3:假设关注点2的条件都不满足,这个时候准备加载Activity自定义动画,通过mapOpenCloseTransitTypes函数映射取出对应的动画类型,也就是AndroidManifest.xml中定义的 动画类型,如activityOpenEnterAnimation

    private static int mapOpenCloseTransitTypes(int transit, boolean enter) {
        int animAttr = 0;
        switch (transit) {
            case TRANSIT_OLD_ACTIVITY_OPEN:
            case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN:
                animAttr = enter
                        ? WindowAnimation_activityOpenEnterAnimation
                        : WindowAnimation_activityOpenExitAnimation;
                break;
    ...
    }

可能我们会好奇transit是怎么来的,这里再稍微深入分析一点

transit: 当前窗口的正在执行的动画类型根据loadAnimation调用栈,transit来源于base/services/core/java/com/android/server/wm/AppTransitionController.java

    void handleAppTransitionReady() {
    ...
            //Get old transit type based on the current transit requests.
            @TransitionOldType final int transit = getTransitCompatType(
                mDisplayContent.mAppTransition, mDisplayContent.mOpeningApps,
                mDisplayContent.mClosingApps, mDisplayContent.mChangingContainers,
                mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
    ...
        try {
            applyAnimations(mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, transit,
                    animLp, voiceInteraction);
    ...
    }

getTransitCompatType: 根据当前窗口的app的请求获取不同的动画类型

本文只关注动画资源加载的逻辑,就不再深入研究不同的窗口动画类型之间的切换了

流程图

总结下是如何加载到自定义资源的

对于当前的窗口是否允许自定义动画流程图如下:

activity自定义资源解析

实现资源解析的类是 base/core/java/com/android/internal/policy/TransitionAnimation.java

    /** Load animation by attribute Id from specific LayoutParams */
    @Nullable
    public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
        int resId = Resources.ID_NULL;
        Context context = mContext;
        if (animAttr >= 0) {
            //1
            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)) {
            //2
            return loadAnimationSafely(context, resId, mTag);
        }
        return null;
    }

总的来说:动画资源是通过读取WindowManager的LayoutParams.windowAnimations而来

关注点1:获取resId的时候,会判断一次window_type

    private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
        if (lp != null && lp.windowAnimations != 0) {
            String packageName = lp.packageName != null ? lp.packageName : DEFAULT_PACKAGE;
            int resId = getAnimationStyleResId(lp);
            if ((resId & 0xFF000000) == 0x01000000) {
                packageName = DEFAULT_PACKAGE;
            }
            return AttributeCache.instance().get(packageName, resId,
                    com.android.internal.R.styleable.WindowAnimation);
        }
        return null;
    }

如果窗口类型为 TYPE_APPLICATION_STARTING , 同样使用默认动画资源

    public int getAnimationStyleResId(@NonNull LayoutParams lp) {
        int resId = lp.windowAnimations;
        if (lp.type == LayoutParams.TYPE_APPLICATION_STARTING) {
            resId = mDefaultWindowAnimationStyleResId;
        }
        return resId;
    }

最后通过传入packageContext和resId获取Activity的动画资源

context: 如果是系统动画,那么就是默认的context, 如果是自定义动画资源,这个context在创建entry的时候创建,源码如下:

                    context = mContext.createPackageContextAsUser(packageName, 0,
                            new UserHandle(userId));

关注点2:最后加载资源的类是 base/core/java/android/view/animation/AnimationUtils.java , 完成从xml到Animation的转换

通过ActivityOptions添加的动画

概述

传入自定义动画

val activityOptions = ActivityOptions.makeCustomAnimation(context, R.anim.slide_in, 0).setLaunchDisplayId(windowContext.displayId)
context.startActivityAsUser(intent, activityOptions.toBundle(), UserHandle.of(windowContext.userProfile.userId))

Options内部将动画类型置为ANIM_CUSTOM , 可以理解为自定义动画

    public static ActivityOptions makeCustomAnimation(Context context,
            int enterResId, int exitResId, int backgroundColor, Handler handler,
            OnAnimationStartedListener listener) {
        ActivityOptions opts = new ActivityOptions();
        opts.mPackageName = context.getPackageName();
        opts.mAnimationType = ANIM_CUSTOM;
        opts.mCustomEnterResId = enterResId;
        opts.mCustomExitResId = exitResId;
        opts.mCustomBackgroundColor = backgroundColor;
        opts.setOnAnimationStartedListener(handler, listener);
        return opts;
    }

所有的动画类型

其中ANIM_REMOTE_ANIMATION是一个远程动画,Launcher启动app时,app图表放大到全屏的过度动画就是这个类型, ActivityOptions多个静态方法对应了多种动画类型

    /** @hide */
    public static final int ANIM_UNDEFINED = -1;
    /** @hide */
    public static final int ANIM_NONE = 0;
    /** @hide */
    public static final int ANIM_CUSTOM = 1;
    /** @hide */
    public static final int ANIM_SCALE_UP = 2;
    /** @hide */
    public static final int ANIM_THUMBNAIL_SCALE_UP = 3;
    /** @hide */
    public static final int ANIM_THUMBNAIL_SCALE_DOWN = 4;
    /** @hide */
    public static final int ANIM_SCENE_TRANSITION = 5;
    /** @hide */
    public static final int ANIM_DEFAULT = 6;
    /** @hide */
    public static final int ANIM_LAUNCH_TASK_BEHIND = 7;
    /** @hide */
    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_UP = 8;
    /** @hide */
    public static final int ANIM_THUMBNAIL_ASPECT_SCALE_DOWN = 9;
    /** @hide */
    public static final int ANIM_CUSTOM_IN_PLACE = 10;
    /** @hide */
    public static final int ANIM_CLIP_REVEAL = 11;
    /** @hide */
    public static final int ANIM_OPEN_CROSS_PROFILE_APPS = 12;
    /** @hide */
    public static final int ANIM_REMOTE_ANIMATION = 13;
    /** @hide */
    public static final int ANIM_FROM_STYLE = 14;

窗口切换动画资源最终会由base/services/core/java/com/android/server/wm/AppTransition.java 类来处理

当Activity生命周期走到 onResume时,在ActivityRecord会触发以下逻辑

sequenceDiagram TaskFragment ->> TaskFragment: resumeTopActivity() TaskFragment ->> ActivityRecord: applyOptionsAnimation() alt ANIM_REMOTE_ANIMATION(远程动画) ActivityRecord ->> AppTransition: overridePendingAppTransitionRemote() else ActivityRecord -->> ActivityRecord: applyOptionsAnimation(options) alt ANIM_CUSTOM(自定义动画) ActivityRecord ->> AppTransition: overridePendingAppTransition() else ANIM_SCALE_UP(缩放动画) note over ActivityRecord,AppTransition: 每个动画有不同名称的函数 ActivityRecord ->> AppTransition: overridePendingAppTransitionScaleUp( end end

AppTransition#overridePendingAppTransition中会保存自定动画的res id和app包名, 后边loadAnimation函数在加载资源的时候会使用

    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
            @ColorInt int backgroundColor, IRemoteCallback startedCallback,
            IRemoteCallback endedCallback, boolean overrideTaskTransaction) {
        if (canOverridePendingAppTransition()) {
            clear();
            mNextAppTransitionOverrideRequested = true;
            mNextAppTransitionPackage = packageName;
            mNextAppTransitionEnter = enterAnim;
            mNextAppTransitionExit = exitAnim;
            mNextAppTransitionBackgroundColor = backgroundColor;
            postAnimationCallback();
            mNextAppTransitionCallback = startedCallback;
            mAnimationFinishedCallback = endedCallback;
            mOverrideTaskTransition = overrideTaskTransaction;
        }
    }

在自定义动画的时候这里发现一个条件: canOverridePendingAppTransition

    private boolean canOverridePendingAppTransition() {
        // Remote animations always take precedence
        return isTransitionSet() &&  mNextAppTransitionType != NEXT_TRANSIT_TYPE_REMOTE;
    }

这个函数大意是,transitionSet不为空,前提是prepareAppTransition函数被调用,开始有一个prepare流程,另外一个条件就是动画类型非远程类型,有了远程动画之后就不会执行自定义动画

options参数

在启动Activity的时候可以传入option参数来指定进入和退出动画资源,自定义动画通常会这样:

ActivityOptions.makeCustomAnimation(context, anim_enter, anim_exit )

根据applyOptionsAnimation的调用栈,我们可以发现 ActivityRecord对这个option做了缓存和解析

    void applyOptionsAnimation() {
        if (mPendingRemoteAnimation != null) {
            //1
            mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
                    mPendingRemoteAnimation);
            mTransitionController.setStatusBarTransitionDelay(
                    mPendingRemoteAnimation.getStatusBarTransitionDelay());
        } else {
            if (mPendingOptions == null
                    || mPendingOptions.getAnimationType() == ANIM_SCENE_TRANSITION) {
                // Scene transition will run on the client side.
                return;
            }
            //2
            applyOptionsAnimation(mPendingOptions, intent);
        }
        clearOptionsAnimationForSiblings();
    }

关注点1:这是系统应用定义的远程动画,具有非常高的优先级,如果是通过makeRemoteAnimation函数传入的动画则会生效

关注点2:传入我们设置的pendingOptions,也就是 ActivityOptions

     /**
     * Apply override app transition base on options & animation type.
     */
    private void applyOptionsAnimation(ActivityOptions pendingOptions, Intent intent) {
        //3
        final int animationType = pendingOptions.getAnimationType();
        final DisplayContent displayContent = getDisplayContent();
        AnimationOptions options = null;
        IRemoteCallback startCallback = null;
        IRemoteCallback finishCallback = null;
        switch (animationType) {
            //4
            case ANIM_CUSTOM:
                displayContent.mAppTransition.overridePendingAppTransition(
                        pendingOptions.getPackageName(),
                        pendingOptions.getCustomEnterResId(),
                        pendingOptions.getCustomExitResId(),
                        pendingOptions.getCustomBackgroundColor(),
                        pendingOptions.getAnimationStartedListener(),
                        pendingOptions.getAnimationFinishedListener(),
                        pendingOptions.getOverrideTaskTransition());
                options = AnimationOptions.makeCustomAnimOptions(pendingOptions.getPackageName(),
                        pendingOptions.getCustomEnterResId(), pendingOptions.getCustomExitResId(),
                        pendingOptions.getCustomBackgroundColor(),
                        pendingOptions.getOverrideTaskTransition());
                startCallback = pendingOptions.getAnimationStartedListener();
                finishCallback = pendingOptions.getAnimationFinishedListener();
                break;
            case ANIM_CLIP_REVEAL:

关注点3:动画类型,上面的options就会生成一个类型为ANIM_CUSTOM的options,当然还有其他缩放平移动画属于系统默认的,由其他case处理

关注点4:overridePendingAppTransition, 直接进入AppTransition类处理

overridePendingAppTransition

    void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
            @ColorInt int backgroundColor, IRemoteCallback startedCallback,
            IRemoteCallback endedCallback, boolean overrideTaskTransaction) {
        if (canOverridePendingAppTransition()) {
            clear();
            mNextAppTransitionOverrideRequested = true;
            mNextAppTransitionPackage = packageName;
            mNextAppTransitionEnter = enterAnim;
            mNextAppTransitionExit = exitAnim;
            mNextAppTransitionBackgroundColor = backgroundColor;
            postAnimationCallback();
            mNextAppTransitionCallback = startedCallback;
            mAnimationFinishedCallback = endedCallback;
            mOverrideTaskTransition = overrideTaskTransaction;
        }
    }

AppTranstion会将进入,进出动画缓存起来,mNextAppTransitionOverrideRequestedmOverrideTaskTransition: 在loadAnimation时候作为override动画的判断条件

options动画的加载

继续关注 loadAnimation函数

    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) {

        //条件1
        if (mNextAppTransitionOverrideRequested) {
            //条件2
            if (canCustomizeAppTransition || mOverrideTaskTransition) {
                mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM;
            } else {
                ProtoLog.e(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: "
                        + " override requested, but it is prohibited by policy.");
            }
        }
        if(...)
        //条件3
        } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
            a = getNextAppRequestedAnimation(enter);
        }
        ....

条件1:首先判断 mNextAppTransitionOverrideRequested 在上面的overridePendingAppTransition已经设置为true

条件2: mOverrideTaskTransition, 只有通过 makeCustomTaskAnimation 创建的动画才是true, 此函数需要系统权限,普通应用调用的makeCustomAnimation是不满足这个条件的

条件3:假设满足1,2两个条件,则会通过getNextAppRequestedAnimation加载传入的自定义动画资源,此函数会传入缓存的动画资源等。

    Animation getNextAppRequestedAnimation(boolean enter) {
        final Animation a = mTransitionAnimation.loadAppTransitionAnimation(
                mNextAppTransitionPackage,
                enter ? mNextAppTransitionEnter : mNextAppTransitionExit);
        ...
        return a;
    }

调用栈

此动画加载和自定义动画加载一样,都是在窗口焦点发生切换的时候触发

通过overridePendingTransition添加的动画

override默认动画

这个函数只有在Activity内部启动另一个Activity才能调用,属于Activity内部方法

    public void overridePendingTransition(int enterAnim, int exitAnim, int backgroundColor) {
        ActivityClient.getInstance().overridePendingTransition(mToken, getPackageName(), enterAnim,
                exitAnim, backgroundColor);
    }

进入了ActivityClient类,最终交给ActivityClientController处理,函数如下:

    public void overridePendingTransition(IBinder token, String packageName,
            int enterAnim, int exitAnim, @ColorInt int backgroundColor) {
                  ...
                r.mDisplayContent.mAppTransition.overridePendingAppTransition(
                        packageName, enterAnim, exitAnim, backgroundColor, null, null,
                        r.mOverrideTaskTransition);
                ...
            }
        }
    }

内部也调用了Apptransition类overridePendingAppTransition函数,接下來的处理逻辑就和ActivityOptions一样了,只不过时机不一样,ActivityOptions先缓存到ActivityRecord,等Activity焦点变换的时候再调用AppTransition处理,而这里直接就传递给AppTransition处理了

load自定义动画

和ActivityOptions动画一样的逻辑

通过registerRemoteAnimations添加的动画

override默认动画

先看入口函数

    public void registerRemoteAnimations(RemoteAnimationDefinition definition) {
        ActivityClient.getInstance().registerRemoteAnimations(mToken, definition);
    }

先进入ActivityClientController函数,最后传递给ActivityRecord

    public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
        ...
                final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
                if (r != null) {
                    r.registerRemoteAnimations(definition);
                }
        ...
    }

    void registerRemoteAnimations(RemoteAnimationDefinition definition) {
        mRemoteAnimationDefinition = definition;
    }

和ActivityOptions一样,先缓存,当Activity焦点变化时,再加载动画

load Remote动画

当页面切换时,触发handleAppTransitionReady函数,和最开始的自定义动画的调用栈一样

    void handleAppTransitionReady() {
        ...
        // Check if there is any override
        //条件1
        if (!overrideWithTaskFragmentRemoteAnimation(transit, activityTypes)) {
            // Unfreeze the windows that were previously frozen for TaskFragment animation.
            unfreezeEmbeddedChangingWindows();
            overrideWithRemoteAnimationIfSet(animLpActivity, transit, activityTypes);
        }
        ...

条件1:检测远程动画,如果有,会更新AppTransition的远程动画变量,函数如下

    private void overrideWithRemoteAnimationIfSet(@Nullable ActivityRecord animLpActivity,
            @TransitionOldType int transit, ArraySet<Integer> activityTypes) {
        RemoteAnimationAdapter adapter = null;
        if (transit == TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE) {
            // The crash transition has higher priority than any involved remote animations.
        } else if (AppTransition.isKeyguardGoingAwayTransitOld(transit)) {
            adapter = mRemoteAnimationDefinition != null
                    ? mRemoteAnimationDefinition.getAdapter(transit, activityTypes)
                    : null;
        //条件2
        } else if (mDisplayContent.mAppTransition.getRemoteAnimationController() == null) {
        //3
            adapter = getRemoteAnimationOverride(animLpActivity, transit, activityTypes);
        }
        if (adapter != null) {
            //4
            mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(adapter);
        }
    }

条件2:如果AppTransition没有远程动画,则会获取Activity的远程动画

关注点3:这里返回的就是ActivityRecord缓存remote动画

emoteAnimationAdapter getRemoteAnimationOverride(@Nullable WindowContainer container,
            @TransitionOldType int transit, ArraySet<Integer> activityTypes) {
        if (container != null) {
            final RemoteAnimationDefinition definition = container.getRemoteAnimationDefinition();
}

关注点4:进入AppTransition的overridePendingAppTransitionRemote函数

    void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter,
            boolean sync, boolean isActivityEmbedding) {
        ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
                        isTransitionSet(), remoteAnimationAdapter);
        if (isTransitionSet() && !mNextAppTransitionIsSync) {
            // ActivityEmbedding animation will run by the app process for which we want to respect
            // the app override for whether or not to show background color.
            clear(!isActivityEmbedding /* clearAppOverride */);
            mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
            mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent,
                    remoteAnimationAdapter, mHandler, isActivityEmbedding);
            mNextAppTransitionIsSync = sync;
        }
    }

设置了mRemoteAnimationController变量

最后做动画的时候会有优先使用Remote动画

    int goodToGo(@TransitionOldType int transit, ActivityRecord topOpeningApp) {
        mNextAppTransitionFlags = 0;

        if (mRemoteAnimationController != null) {
            mRemoteAnimationController.goodToGo(transit);
        }
        ...

当我们设置了远程动画之后,其他动画都会失效,禁止override


    private boolean canOverridePendingAppTransition() {
        // Remote animations always take precedence
        return isTransitionSet() &&  mNextAppTransitionType != NEXT_TRANSIT_TYPE_REMOTE;
    }

Launcher在启动应用的时候有一个全局过度动画,就是设置的这个动画

FLAG_ACTIVITY_NO_ANIMATION禁用动画

flag设置

startActivity的时候加入flag Intent.FLAG_ACTIVITY_NO_ANIMATION, 禁用窗口动画

    void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTask,
            boolean isTaskSwitch, ActivityOptions options, @Nullable ActivityRecord sourceRecord) {
            ...
            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                dc.prepareAppTransition(TRANSIT_NONE);
                mTaskSupervisor.mNoAnimActivities.add(r);
            }

startActivity的时候 调用prepareAppTransition(TRANSIT_NONE),设置为无动画状态

在loadAnimation的时候会对transit做一次转换,得到animAttr=0

    private static int mapOpenCloseTransitTypes(int transit, boolean enter) {
        int animAttr = 0;
        switch (transit) {
        ...
        }
        return animAttr

loadAnimation会返回null,则不会执行动画效果

    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) {
            ...
            int animAttr = mapOpenCloseTransitTypes(transit, enter);
            a = animAttr == 0 ? null
            return animAttr;
    }

加载默认动画资源

AppTransition#loadAnimation

    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) {
            ...
            a = animAttr == 0 ? null : (canCustomizeAppTransition
                ? loadAnimationAttr(lp, animAttr, transit)
                //1
                : mTransitionAnimation.loadDefaultAnimationAttr(animAttr, transit));

            return animAttr;
    }

关注点1:loadDefaultAnimationAttr

    public Animation loadDefaultAnimationAttr(int animAttr, @TransitionOldType int transit) {
        return loadAnimationAttr(DEFAULT_PACKAGE, mDefaultWindowAnimationStyleResId, animAttr,
                false /* translucent */, transit);
    }

默认的包名和attr定义如下:

    private static final String DEFAULT_PACKAGE = "android";

        mDefaultWindowAnimationStyleResId = windowStyle.getResourceId(
                com.android.internal.R.styleable.Window_windowAnimationStyle, 0);

ActivityOptions

调用栈

applyOptionsAnimation调用栈

01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.ActivityRecord.applyOptionsAnimation(ActivityRecord.java:4878)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.TaskFragment.resumeTopActivity(TaskFragment.java:1375)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.Task.resumeTopActivityInnerLocked(Task.java:5050)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:4980)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:5031)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:6034)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.RootWindowContainer.lambda$resumeFocusedTasksTopActivities$19(RootWindowContainer.java:2538)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda16.accept(Unknown Source:13)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.Task.forAllRootTasks(Task.java:3182)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2108)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2108)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2108)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2108)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2108)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2108)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2101)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2519)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2497)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2492)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.Task.moveTaskToBackInner(Task.java:5758)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.Task.moveTaskToBack(Task.java:5734)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.ActivityClientController.moveActivityTaskToBack(ActivityClientController.java:325)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at android.app.IActivityClientController$Stub.onTransact(IActivityClientController.java:668)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.ActivityClientController.onTransact(ActivityClientController.java:127)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at android.os.Binder.execTransactInternal(Binder.java:1280)
01-07 11:23:46.485 18087 19833 W APP_ANIMATION_DEBUG: 	at android.os.Binder.execTransact(Binder.java:1244)

applyOptionsAnimation 会调用AppTransition#overridePendingAppTransition,设置对应的动画类型,最后在loadAnimation的时候 根据不同的动画类型加载不同的动画资源

loadAnimation调用栈

 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.AppTransition.loadAnimation(AppTransition.java:781)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.loadAnimation(WindowContainer.java:3284)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.getAnimationAdapter(WindowContainer.java:3111)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.applyAnimationUnchecked(WindowContainer.java:3156)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.Task.applyAnimationUnchecked(Task.java:3371)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowContainer.applyAnimation(WindowContainer.java:3000)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.AppTransitionController.applyAnimations(AppTransitionController.java:918)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.AppTransitionController.applyAnimations(AppTransitionController.java:1121)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.AppTransitionController.handleAppTransitionReady(AppTransitionController.java:302)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.RootWindowContainer.checkAppTransitionReady(RootWindowContainer.java:987)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:850)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.RootWindowContainer.performSurfacePlacement(RootWindowContainer.java:793)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:177)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:126)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:115)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.wm.WindowSurfacePlacer$Traverser.run(WindowSurfacePlacer.java:57)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at android.os.Handler.handleCallback(Handler.java:942)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at android.os.Handler.dispatchMessage(Handler.java:99)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at android.os.Looper.loopOnce(Looper.java:201)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at android.os.Looper.loop(Looper.java:288)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at android.os.HandlerThread.run(HandlerThread.java:67)
01-07 11:23:46.546 18087 18155 W APP_ANIMATION_DEBUG: 	at com.android.server.ServiceThread.run(ServiceThread.java:44)

loadAnimation在窗口切换的时候由AppTransitionController触发

参考资料

[1]:【Android 13源码分析】应用启动动画-app_transition-2

春风花气馥,秋月寒江湛