AOSP 14 appTransition 窗口关闭动画流程

7 阅读22分钟

AOSP 14 Activity 默认关闭动画详细代码流程分析

概述

本文档详细分析在未开启 Shell Transition的情况下,AOSP 14 中 Activity 默认关闭动画的完整设置和加载流程。


一、完整调用链概览

[App 进程]
Activity.finish()
    │
    ▼
Activity.finish(int finishTask)                              [Activity.java:7018]
    │
    ▼
ActivityClient.finishActivity()                              [ActivityClient.java:172]
    │ (Binder IPC)
    ▼
[System Server 进程]
ActivityClientController.finishActivity()                    [ActivityClientController.java:424]
    │
    ▼
ActivityRecord.finishIfPossible(int, Intent, NeededUriGrants, String, boolean)  [ActivityRecord.java:3496]
    │
    ├─► DisplayContent.prepareAppTransition(TRANSIT_CLOSE)   [ActivityRecord.java:3568]
    │       │
    │       ▼
    │   DisplayContent.prepareAppTransition(int, int)        [DisplayContent.java:5608]
    │       │
    │       ▼
    │   AppTransition.prepareAppTransition(int, int)         [AppTransition.java:1480]
    │
    ├─► ActivityRecord.setVisibility(false)                  [ActivityRecord.java:3584]
    │       │
    │       ▼
    │   ActivityRecord.setVisibility(boolean, boolean)       [ActivityRecord.java:5307]
    │       │
    │       ▼
    │   ActivityRecord.deferCommitVisibilityChange(false)    [ActivityRecord.java:5455]
    │       │
    │       ▼
    │   DisplayContent.mClosingApps.add(this)                [ActivityRecord.java:5497]
    │
    ▼
AppTransitionController.handleAppTransitionReady()          [AppTransitionController.java:172]
    │
    ├─► AppTransitionController.transitionGoodToGo()         [AppTransitionController.java:174]
    │
    ├─► AppTransitionController.getTransitCompatType()       [AppTransitionController.java:263]
    │       │
    │       ▼
    │   AppTransitionController.getAnimationTargets()        [AppTransitionController.java:475-476]
    │       │
    │       ▼
    │   AppTransitionController.getTransitContainerType()    [AppTransitionController.java:547]
    │       返回 TRANSIT_OLD_ACTIVITY_CLOSE                  [AppTransitionController.java:530]
    │
    ├─► AppTransitionController.findAnimLayoutParamsToken()  [AppTransitionController.java:279]
    │       │
    │       ▼
    │   AppTransitionController.getAnimLp()                  [AppTransitionController.java:560]
    │
    ├─► AppTransitionController.applyAnimations(ArraySet<ActivityRecord>, ...)  [AppTransitionController.java:300]
    │       │
    │       ▼
    │   AppTransitionController.getAnimationTargets()        [AppTransitionController.java:1107-1109]
    │       │
    │       ▼
    │   AppTransitionController.applyAnimations(ArraySet<WindowContainer>, ...) [AppTransitionController.java:909]
    │       │
    │       ▼
    │   WindowContainer.applyAnimation(LayoutParams, int, boolean, boolean, ArrayList) [WindowContainer.java:3092]
    │       │
    │       ▼
    │   WindowContainer.applyAnimationUnchecked(LayoutParams, boolean, int, boolean, ArrayList) [WindowContainer.java:3253]
    │           │
    │           ▼
    │       WindowContainer.getAnimationAdapter(LayoutParams, int, boolean, boolean) [WindowContainer.java:3097]
    │           │
    │           ▼
    │       WindowContainer.loadAnimation(LayoutParams, int, boolean, boolean) [WindowContainer.java:3355]
    │           │
    │           ▼
    │       AppTransition.loadAnimation(LayoutParams, int, boolean, int, int, Rect, ...) [AppTransition.java:764]
    │           │
    │           ├─► AppTransition.mapOpenCloseTransitTypes(int, boolean)  [AppTransition.java:892]
    │           │       返回 activityCloseExitAnimation                  [AppTransition.java:565-566]
    │           │
    │           └─► TransitionAnimation.loadDefaultAnimationAttr(int, int) [AppTransition.java:897]
    │                   │
    │                   ▼
    │               TransitionAnimation.loadAnimationAttr(String, int, int, boolean, int) [TransitionAnimation.java:348]
    │
    ├─► AppTransitionController.handleClosingApps()          [AppTransitionController.java:302]
    │       │
    │       ▼
    │   ActivityRecord.commitVisibility(false, boolean)      [AppTransitionController.java:1208]
    │       │
    │       ▼
    │   ActivityRecord.updateReportedVisibilityLocked()      [AppTransitionController.java:1209]
    │
    ├─► AppTransitionController.handleOpeningApps()          [AppTransitionController.java:303]
    │       │
    │       ▼
    │   ActivityRecord.commitVisibility(true, boolean)       [AppTransitionController.java:1163]
    │       │
    │       ▼
    │   ActivityRecord.showAllWindowsLocked()                [AppTransitionController.java:1180]
    │
    └─► AppTransitionController.handleChangingApps(int)      [AppTransitionController.java:304]

二、详细代码分析(逐步跟踪)

步骤 1: App 进程发起 finish 请求

1.1 Activity.finish()

文件: frameworks/base/core/java/android/app/Activity.java

// 第 7018-7020 行
public void finish() {
    finish(DONT_FINISH_TASK_WITH_ACTIVITY);
}

说明: 用户调用 Activity.finish() 方法,内部调用重载方法 finish(int finishTask),参数 DONT_FINISH_TASK_WITH_ACTIVITY 表示只 finish 当前 Activity,不 finish 整个 Task。


1.2 Activity.finish(int finishTask)

文件: frameworks/base/core/java/android/app/Activity.java

// 第 6990-7008 行
private void finish(int finishTask) {
    if (mParent == null) {
        int resultCode;
        Intent resultData;
        synchronized (this) {
            resultCode = mResultCode;
            resultData = mResultData;
        }
        if (false) Log.v(TAG, "Finishing self: token=" + mToken);
        if (resultData != null) {
            resultData.prepareToLeaveProcess(this);
        }
        // 关键调用:通过 ActivityClient 发起 finish 请求
        if (ActivityClient.getInstance().finishActivity(mToken, resultCode, resultData,
                finishTask)) {
            mFinished = true;
        }
    } else {
        mParent.finishFromChild(this);
    }

    getAutofillClientController().onActivityFinish(mIntent);
}

说明: 此方法收集 Activity 的结果码和结果数据,然后通过 ActivityClient.getInstance().finishActivity() 发起 Binder IPC 调用到 System Server。


1.3 ActivityClient.finishActivity()

文件: frameworks/base/core/java/android/app/ActivityClient.java

// 第 172-179 行
public boolean finishActivity(IBinder token, int resultCode, Intent resultData,
        int finishTask) {
    try {
        // 关键调用:通过 Binder 调用 ActivityClientController 的 finishActivity 方法
        return getActivityClientController().finishActivity(token, resultCode, resultData,
                finishTask);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

说明: ActivityClient 是 App 进程与 System Server 通信的客户端代理,通过 getActivityClientController() 获取 IActivityClientController 的 Binder 代理,发起跨进程调用。


步骤 2: System Server 处理 finish 请求

2.1 ActivityClientController.finishActivity()

文件: frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java

// 第 424-509 行
@Override
public boolean finishActivity(IBinder token, int resultCode, Intent resultData,
        int finishTask) {
    // Refuse possible leaked file descriptors.
    if (resultData != null && resultData.hasFileDescriptors()) {
        throw new IllegalArgumentException("File descriptors passed in Intent");
    }

    final ActivityRecord r;
    synchronized (mGlobalLock) {
        // 根据 token 查找对应的 ActivityRecord
        r = ActivityRecord.isInRootTaskLocked(token);
        if (r == null) {
            return true;
        }
    }

    // Carefully collect grants without holding lock.
    final NeededUriGrants resultGrants = mService.collectGrants(resultData, r.resultTo);

    synchronized (mGlobalLock) {
        // Check again in case activity was removed when collecting grants.
        if (!r.isInHistory()) {
            return true;
        }

        // Keep track of the root activity of the task before we finish it.
        final Task tr = r.getTask();
        final ActivityRecord rootR = tr.getRootActivity();
        if (rootR == null) {
            Slog.w(TAG, "Finishing task with all activities already finished");
        }
        // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can
        // finish.
        if (mService.getLockTaskController().activityBlockedFromFinish(r)) {
            return false;
        }

        // ... 省略部分代码 ...

        final long origId = Binder.clearCallingIdentity();
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
        try {
            final boolean res;
            final boolean finishWithRootActivity =
                    finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
            if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
                    || (finishWithRootActivity && r == rootR)) {
                // ... 处理 finish task 的情况 ...
            } else {
                // 关键调用:调用 ActivityRecord.finishIfPossible
                r.finishIfPossible(resultCode, resultData, resultGrants, "app-request",
                        true /* oomAdj */);
                res = r.finishing;
                if (!res) {
                    Slog.i(TAG, "Failed to finish by app-request");
                }
            }
            return res;
        } finally {
            Trace.traceEnd(Trace.TAG_WINDOW_MANAGER);
            Binder.restoreCallingIdentity(origId);
        }
    }
}

说明:

  • 此方法在 System Server 进程中执行
  • 通过 ActivityRecord.isInRootTaskLocked(token) 根据 token 查找对应的 ActivityRecord
  • 最终调用 r.finishIfPossible() 方法处理 finish 逻辑

步骤 3: ActivityRecord.finishIfPossible() 准备关闭过渡

3.1 finishIfPossible 方法入口

文件: frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

// 第 3496-3520 行
@FinishRequest int finishIfPossible(int resultCode, Intent resultData,
            NeededUriGrants resultGrants, String reason, boolean oomAdj) {
    ProtoLog.v(WM_DEBUG_STATES, "Finishing activity r=%s, result=%d, data=%s, "
            + "reason=%s", this, resultCode, resultData, reason);

    if (finishing) {
        Slog.w(TAG, "Duplicate finish request for r=" + this);
        return FINISH_RESULT_CANCELLED;
    }

    if (!isInRootTaskLocked()) {
        Slog.w(TAG, "Finish request when not in root task for r=" + this);
        return FINISH_RESULT_CANCELLED;
    }

    final Task rootTask = getRootTask();
    final boolean mayAdjustTop = (isState(RESUMED) || rootTask.getTopResumedActivity() == null)
            && rootTask.isFocusedRootTaskOnDisplay()
            && !task.isClearingToReuseTask();
    final boolean shouldAdjustGlobalFocus = mayAdjustTop
            && mRootWindowContainer.isTopDisplayFocusedRootTask(rootTask);

    mAtmService.deferWindowLayout();
    try {
        mTaskSupervisor.mNoHistoryActivities.remove(this);
        makeFinishingLocked();  // 标记 Activity 为 finishing 状态
        // ... 继续执行 ...

说明:

  • 方法首先检查是否已经是 finishing 状态,避免重复 finish
  • 调用 makeFinishingLocked() 将 Activity 标记为 finishing 状态
  • 调用 mAtmService.deferWindowLayout() 延迟窗口布局

3.2 准备关闭过渡动画

文件: frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

// 第 3550-3590 行
        // ... 前面的代码 ...

        finishActivityResults(resultCode, resultData, resultGrants);

        final boolean endTask = task.getTopNonFinishingActivity() == null
                && !task.isClearingToReuseTask();
        
        // 请求关闭过渡(用于 Shell Transition,如果未启用则返回 null)
        final Transition newTransition =
                mTransitionController.requestCloseTransitionIfNeeded(endTask ? task : this);
        
        if (isState(RESUMED)) {
            if (endTask) {
                mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
                        task.getTaskInfo());
            }
            // Prepare app close transition, but don't execute just yet. It is possible that
            // an activity that will be made resumed in place of this one will immediately
            // launch another new activity. In this case current closing transition will be
            // combined with open transition for the new activity.
            if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
                Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
            }
            
            // ★★★ 关键调用:声明关闭过渡类型 ★★★
            mDisplayContent.prepareAppTransition(TRANSIT_CLOSE);

            // When finishing the activity preemptively take the snapshot before the app window
            // is marked as hidden and any configuration changes take place
            // Note that RecentsAnimation will handle task snapshot while switching apps with
            // the best capture timing (e.g. IME window capture),
            // No need additional task capture while task is controlled by RecentsAnimation.
            if (!mTransitionController.isShellTransitionsEnabled()
                    && !task.isAnimatingByRecents()) {
                final ArraySet<Task> tasks = Sets.newArraySet(task);
                mAtmService.mWindowManager.mTaskSnapshotController.snapshotTasks(tasks);
                mAtmService.mWindowManager.mTaskSnapshotController
                        .addSkipClosingAppSnapshotTasks(tasks);
            }

            // Tell window manager to prepare for this one to be removed.
            // ★★★ 关键调用:设置 Activity 不可见 ★★★
            setVisibility(false);

说明:

  • 第 3568 行: mDisplayContent.prepareAppTransition(TRANSIT_CLOSE) - 声明关闭过渡类型
  • 第 3584 行: setVisibility(false) - 设置 Activity 不可见,这会将 Activity 加入到 mClosingApps 列表

步骤 4: prepareAppTransition 声明关闭过渡

4.1 DisplayContent.prepareAppTransition(int)

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

// 第 5608-5613 行
void prepareAppTransition(@WindowManager.TransitionType int transit) {
    prepareAppTransition(transit, 0 /* flags */);
}

说明: 重载方法,调用带有 flags 参数的版本。


4.2 DisplayContent.prepareAppTransition(int, int)

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

// 第 5615-5621 行
void prepareAppTransition(@WindowManager.TransitionType int transit,
        @WindowManager.TransitionFlags int flags) {
    final boolean prepared = mAppTransition.prepareAppTransition(transit, flags);
    if (prepared && okToAnimate() && transit != TRANSIT_NONE) {
        mSkipAppTransitionAnimation = false;
    }
}

说明: 调用 AppTransition.prepareAppTransition() 方法。


4.3 AppTransition.prepareAppTransition(int, int)

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

// 第 1480-1494 行
boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) {
    if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
        return false;  // 如果启用了 Shell Transition,直接返回
    }
    // ★★★ 关键:将 TRANSIT_CLOSE 添加到请求列表 ★★★
    mNextAppTransitionRequests.add(transit);
    mNextAppTransitionFlags |= flags;
    updateBooster();
    removeAppTransitionTimeoutCallbacks();
    mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable,
            APP_TRANSITION_TIMEOUT_MS);
    return prepare();
}

说明:

  • 检查是否启用了 Shell Transition,如果启用则直接返回 false
  • 将过渡类型 TRANSIT_CLOSE 添加到 mNextAppTransitionRequests 列表中
  • 设置超时回调,防止过渡动画无限等待

步骤 5: setVisibility(false) 将 Activity 加入关闭列表

5.1 ActivityRecord.setVisibility(boolean)

文件: frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

// 第 5289-5305 行
void setVisibility(boolean visible) {
    if (getParent() == null) {
        Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: " + token);
        return;
    }
    if (visible == mVisibleRequested && visible == mVisible && visible == isClientVisible()
            && mTransitionController.isShellTransitionsEnabled()) {
        // For shell transition, it is no-op if there is no state change.
        return;
    }
    if (visible) {
        mDeferHidingClient = false;
    }
    // 调用内部方法
    setVisibility(visible, mDeferHidingClient);
    mAtmService.addWindowLayoutReasons(
            ActivityTaskManagerService.LAYOUT_REASON_VISIBILITY_CHANGED);
    mTaskSupervisor.getActivityMetricsLogger().notifyVisibilityChanged(this);
    mTaskSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
}

说明: setVisibility(boolean visible) 调用内部重载方法 setVisibility(boolean visible, boolean deferHidingClient)


5.2 ActivityRecord.setVisibility(boolean, boolean)

文件: frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

// 第 5307-5455 行
private void setVisibility(boolean visible, boolean deferHidingClient) {
    final AppTransition appTransition = getDisplayContent().mAppTransition;

    // Don't set visibility to false if we were already not visible. This prevents WM from
    // adding the app to the closing app list which doesn't make sense for something that is
    // already not visible.
    if (!visible && !mVisibleRequested) {
        // ... 省略部分代码 ...
        return;
    }

    ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
            "setAppVisibility(%s, visible=%b): %s visible=%b mVisibleRequested=%b Callers=%s",
            token, visible, appTransition, isVisible(), mVisibleRequested,
            Debug.getCallers(6));

    // ... 省略中间代码 ...

    // 从 openingApps 和 closingApps 中移除自己(避免重复)
    displayContent.mOpeningApps.remove(this);
    displayContent.mClosingApps.remove(this);
    waitingToShow = false;
    setVisibleRequested(visible);
    mLastDeferHidingClient = deferHidingClient;

    // ... 省略中间代码 ...

    // 如果正在收集过渡动画,延迟提交可见性变化
    if (isCollecting) {
        return;
    }
    if (inFinishingTransition) {
        mTransitionController.mValidateCommitVis.add(this);
        return;
    }
    
    // ★★★ 关键调用:延迟提交可见性变化 ★★★
    if (deferCommitVisibilityChange(visible)) {
        return;
    }

    commitVisibility(visible, true /* performLayout */);
    updateReportedVisibilityLocked();
}

说明:

  • 先从 mOpeningAppsmClosingApps 中移除自己,避免重复
  • 调用 deferCommitVisibilityChange(visible) 决定是否延迟提交可见性变化

5.3 ActivityRecord.deferCommitVisibilityChange(boolean)

文件: frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

// 第 5457-5510 行
private boolean deferCommitVisibilityChange(boolean visible) {
    if (mTransitionController.isShellTransitionsEnabled()) {
        // Shell transition doesn't use opening/closing sets.
        return false;
    }
    if (!mDisplayContent.mAppTransition.isTransitionSet()) {
        // Defer committing visibility for non-home app which is animating by recents.
        if (isActivityTypeHome() || !isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
            return false;
        }
    }
    if (mWaitForEnteringPinnedMode && mVisible == visible) {
        return false;
    }

    // The animation will be visible soon so do not skip by screen off.
    final boolean ignoreScreenOn = canTurnScreenOn() || mTaskSupervisor.getKeyguardController()
            .isKeyguardGoingAway(mDisplayContent.mDisplayId);
    // Ignore display frozen so the opening / closing transition type can be updated correctly
    // even if the display is frozen.
    if (!okToAnimate(true /* ignoreFrozen */, ignoreScreenOn)) {
        return false;
    }
    
    // ★★★ 关键:根据 visible 决定加入 openingApps 还是 closingApps ★★★
    if (visible) {
        // 如果是要变为可见,加入 openingApps
        mDisplayContent.mOpeningApps.add(this);
        mEnteringAnimation = true;
    } else if (mVisible) {
        // ★★★ 如果是要变为不可见且当前可见,加入 closingApps ★★★
        mDisplayContent.mClosingApps.add(this);
        mEnteringAnimation = false;
    }
    
    // ... 省略部分代码 ...
    
    return true;
}

说明:

  • 第 5495-5497 行: 这是关键代码,当 visible=false 且当前 mVisible=true 时,将 Activity 加入到 mClosingApps 列表
  • 此方法返回 true,表示延迟提交可见性变化,等待过渡动画开始后再提交

步骤 6: 过渡动画触发时机

当 Activity 设置为不可见后,系统会等待过渡动画准备好,然后调用 handleAppTransitionReady()

过渡动画触发的时机有两种情况:

  1. RESUMED 状态的 Activity finish: 会触发 startPausing(),等待 pause 完成后触发过渡
  2. 非 RESUMED 状态的 Activity finish: 会调用 prepareActivityHideTransitionAnimation() 直接执行过渡
6.1 ActivityRecord.prepareActivityHideTransitionAnimation()

文件: frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

// 第 3667-3672 行
private void prepareActivityHideTransitionAnimation() {
    final DisplayContent dc = mDisplayContent;
    dc.prepareAppTransition(TRANSIT_CLOSE);  // 声明关闭过渡
    setVisibility(false);                     // 设置不可见
    dc.executeAppTransition();                // 立即执行过渡
}

6.2 DisplayContent.executeAppTransition()

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

// 第 5634-5643 行
void executeAppTransition() {
    mTransitionController.setReady(this);
    if (mAppTransition.isTransitionSet()) {
        ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
                "Execute app transition: %s, displayId: %d Callers=%s",
                mAppTransition, mDisplayId, Debug.getCallers(5));
        mAppTransition.setReady();
        mWmService.mWindowPlacerLocked.requestTraversal();
    }
}

说明: 调用 mAppTransition.setReady() 标记过渡准备完成,然后请求布局遍历,这会触发 handleAppTransitionReady()


步骤 7: AppTransitionController.handleAppTransitionReady() 处理过渡

7.1 handleAppTransitionReady() 方法入口

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 172-180 行
void handleAppTransitionReady() {
    mTempTransitionReasons.clear();
    
    // ★★★ 关键调用:检查所有打开的 App 是否准备好 ★★★
    if (!transitionGoodToGo(mDisplayContent.mOpeningApps, mTempTransitionReasons)
            || !transitionGoodToGo(mDisplayContent.mChangingContainers, mTempTransitionReasons)
            || !transitionGoodToGoForTaskFragments()) {
        return;  // 如果没准备好,等待下次调用
    }
    
    // ... 省略部分代码 ...

说明: 首先调用 transitionGoodToGo() 检查所有参与过渡的 App 是否准备好。


7.2 AppTransitionController.transitionGoodToGo()

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 1287-1380 行
private boolean transitionGoodToGo(ArraySet<? extends WindowContainer> apps,
        ArrayMap<WindowContainer, Integer> outReasons) {
    ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
            "Checking %d opening apps (frozen=%b timeout=%b)...", apps.size(),
            mService.mDisplayFrozen, mDisplayContent.mAppTransition.isTimeout());
    if (mDisplayContent.mAppTransition.isTimeout()) {
        return true;  // 超时则直接开始
    }
    
    // ... 检查屏幕旋转动画 ...
    
    for (int i = 0; i < apps.size(); i++) {
        WindowContainer wc = apps.valueAt(i);
        // ... 检查每个窗口是否准备好 ...
    }
    return true;
}

说明: 检查所有参与过渡的窗口容器是否准备好。


7.3 清理动画标志和调整壁纸

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 218-250 行
        // ... 前面的代码 ...

        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "AppTransitionReady");

        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
        // 清理动画退出状态的窗口
        mDisplayContent.forAllWindows(WindowState::cleanupAnimatingExitWindow,
                true /* traverseTopToBottom */);
        
        final AppTransition appTransition = mDisplayContent.mAppTransition;

        mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();

        appTransition.removeAppTransitionTimeoutCallbacks();

        mDisplayContent.mWallpaperMayChange = false;

        int appCount = mDisplayContent.mOpeningApps.size();
        for (int i = 0; i < appCount; ++i) {
            // 清除 openingApps 的动画标志
            mDisplayContent.mOpeningApps.valueAtUnchecked(i).clearAnimatingFlags();
        }
        appCount = mDisplayContent.mChangingContainers.size();
        for (int i = 0; i < appCount; ++i) {
            final ActivityRecord activity = getAppFromContainer(
                    mDisplayContent.mChangingContainers.valueAtUnchecked(i));
            if (activity != null) {
                activity.clearAnimatingFlags();
            }
        }

        // 调整壁纸窗口
        mWallpaperControllerLocked.adjustWallpaperWindowsForAppTransitionIfNeeded(
                mDisplayContent.mOpeningApps);

说明:

  • 调用 WindowState.cleanupAnimatingExitWindow() 清理动画退出状态的窗口
  • 调用 clearAnimatingFlags() 清除动画标志
  • 调用 adjustWallpaperWindowsForAppTransitionIfNeeded() 调整壁纸窗口

步骤 8: 确定过渡类型

8.1 获取过渡类型

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 252-275 行
        ArraySet<ActivityRecord> tmpOpenApps = mDisplayContent.mOpeningApps;
        ArraySet<ActivityRecord> tmpCloseApps = mDisplayContent.mClosingApps;
        if (mDisplayContent.mAtmService.mBackNavigationController.isMonitoringTransition()) {
            tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps);
            tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
            if (mDisplayContent.mAtmService.mBackNavigationController
                    .removeIfContainsBackAnimationTargets(tmpOpenApps, tmpCloseApps)) {
                mDisplayContent.mAtmService.mBackNavigationController.clearBackAnimations();
            }
        }

        // ★★★ 关键调用:确定过渡类型 ★★★
        @TransitionOldType final int transit = getTransitCompatType(
                mDisplayContent.mAppTransition, tmpOpenApps,
                tmpCloseApps, mDisplayContent.mChangingContainers,
                mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
                mDisplayContent.mSkipAppTransitionAnimation);
        mDisplayContent.mSkipAppTransitionAnimation = false;

        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                "handleAppTransitionReady: displayId=%d appTransition={%s}"
                + " openingApps=[%s] closingApps=[%s] transit=%s",
                mDisplayContent.mDisplayId, appTransition.toString(), tmpOpenApps,
                tmpCloseApps, AppTransition.appTransitionOldToString(transit));

说明: 调用 getTransitCompatType() 方法确定具体的过渡类型。


8.2 AppTransitionController.getTransitCompatType()

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 360-545 行
@TransitionOldType static int getTransitCompatType(AppTransition appTransition,
        ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
        ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget,
        @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) {

    final ActivityRecord topOpeningApp = getTopApp(openingApps, false /* ignoreHidden */);
    final ActivityRecord topClosingApp = getTopApp(closingApps, true /* ignoreHidden */);

    // ... 省略 keyguard 和 dream 类型的处理 ...

    @TransitionFlags final int flags = appTransition.getTransitFlags();
    @TransitionType final int firstTransit = appTransition.getFirstAppTransition();

    // ... 省略特殊过渡类型的处理 ...

    // ★★★ 关键调用:获取动画目标容器 ★★★
    final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
            openingApps, closingApps, true /* visible */);
    final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
            openingApps, closingApps, false /* visible */);
    final WindowContainer<?> openingContainer = !openingWcs.isEmpty()
            ? openingWcs.valueAt(0) : null;
    final WindowContainer<?> closingContainer = !closingWcs.isEmpty()
            ? closingWcs.valueAt(0) : null;
    
    // ★★★ 关键调用:获取容器类型 ★★★
    @TransitContainerType int openingType = getTransitContainerType(openingContainer);
    @TransitContainerType int closingType = getTransitContainerType(closingContainer);
    
    // ... 省略 TRANSIT_TO_FRONT 和 TRANSIT_TO_BACK 的处理 ...

    if (appTransition.containsTransitRequest(TRANSIT_OPEN)) {
        // ... 处理打开过渡 ...
    }
    
    // ★★★ 关键:处理关闭过渡 ★★★
    if (appTransition.containsTransitRequest(TRANSIT_CLOSE)) {
        if (closingType == TYPE_TASK) {
            return TRANSIT_OLD_TASK_CLOSE;  // Task 关闭
        }
        if (closingType == TYPE_TASK_FRAGMENT) {
            return TRANSIT_OLD_TASK_FRAGMENT_CLOSE;  // TaskFragment 关闭
        }
        if (closingType == TYPE_ACTIVITY) {
            // 遍历 closingApps,检查是否有可见的 Activity
            for (int i = closingApps.size() - 1; i >= 0; i--) {
                if (closingApps.valueAt(i).visibleIgnoringKeyguard) {
                    // ★★★ 返回 Activity 关闭过渡类型 ★★★
                    return TRANSIT_OLD_ACTIVITY_CLOSE;
                }
            }
            // 如果没有可见的关闭 App,跳过过渡动画
            return WindowManager.TRANSIT_OLD_UNSET;
        }
    }
    // ... 省略其他过渡类型 ...

    return TRANSIT_OLD_NONE;
}

说明:

  • 调用 getAnimationTargets() 获取动画目标容器
  • 调用 getTransitContainerType() 获取容器类型(TYPE_ACTIVITY, TYPE_TASK, TYPE_TASK_FRAGMENT)
  • 对于 Activity 关闭,返回 TRANSIT_OLD_ACTIVITY_CLOSE

8.3 AppTransitionController.getAnimationTargets()

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 950-1070 行
private ArraySet<WindowContainer> getAnimationTargets(
        ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
        boolean visible) {
    // 收集候选容器
    final ArraySet<WindowContainer> candidates = new ArraySet<>();
    final ArraySet<ActivityRecord> apps = visible ? openingApps : closingApps;
    for (int i = 0; i < apps.size(); i++) {
        final ActivityRecord app = apps.valueAt(i);
        if (app.shouldApplyAnimation(visible)) {
            candidates.add(app);
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                    "Changing app %s visible=%b performLayout=%b",
                    app, app.isVisible(), false);
        }
    }
    
    // ... 省略动画目标提升逻辑 ...
    
    return targets;
}

说明: 获取需要应用动画的 WindowContainer 集合。


8.4 AppTransitionController.getTransitContainerType()

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 547-560 行
@TransitContainerType
private static int getTransitContainerType(@Nullable WindowContainer<?> container) {
    if (container == null) {
        return TYPE_NONE;
    }
    if (container.asTask() != null) {
        return TYPE_TASK;
    }
    if (container.asTaskFragment() != null) {
        return TYPE_TASK_FRAGMENT;
    }
    if (container.asActivityRecord() != null) {
        return TYPE_ACTIVITY;  // ★★★ Activity 类型 ★★★
    }
    return TYPE_NONE;
}

说明: 判断容器的类型,对于 ActivityRecord 返回 TYPE_ACTIVITY


步骤 9: 获取动画布局参数

9.1 收集 Activity 类型

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 277-280 行
        // Find the layout params of the top-most application window in the tokens, which is
        // what will control the animation theme.
        final ArraySet<Integer> activityTypes = collectActivityTypes(tmpOpenApps,
                tmpCloseApps, mDisplayContent.mChangingContainers);

9.2 AppTransitionController.collectActivityTypes()

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 842-857 行
private static ArraySet<Integer> collectActivityTypes(ArraySet<ActivityRecord> array1,
        ArraySet<ActivityRecord> array2, ArraySet<WindowContainer> array3) {
    final ArraySet<Integer> result = new ArraySet<>();
    for (int i = array1.size() - 1; i >= 0; i--) {
        result.add(array1.valueAt(i).getActivityType());
    }
    for (int i = array2.size() - 1; i >= 0; i--) {
        result.add(array2.valueAt(i).getActivityType());
    }
    for (int i = array3.size() - 1; i >= 0; i--) {
        result.add(array3.valueAt(i).getActivityType());
    }
    return result;
}

9.3 AppTransitionController.findAnimLayoutParamsToken()

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 810-840 行
@Nullable
private ActivityRecord findAnimLayoutParamsToken(@TransitionOldType int transit,
        ArraySet<Integer> activityTypes, ArraySet<ActivityRecord> openingApps,
        ArraySet<ActivityRecord> closingApps, ArraySet<WindowContainer> changingApps) {
    ActivityRecord result;

    // Remote animations always win, but fullscreen tokens override non-fullscreen tokens.
    result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
            w -> w.getRemoteAnimationDefinition() != null
                    && w.getRemoteAnimationDefinition().hasTransition(transit, activityTypes));
    if (result != null) {
        return result;
    }
    // 查找全屏窗口
    result = lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
            w -> w.fillsParent() && w.findMainWindow() != null);
    if (result != null) {
        return result;
    }
    // 查找任何有主窗口的 Activity
    return lookForHighestTokenWithFilter(closingApps, openingApps, changingApps,
            w -> w.findMainWindow() != null);
}

说明: 找到用于控制动画主题的 Activity。


9.4 AppTransitionController.getAnimLp()

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 560-564 行
@Nullable
private static WindowManager.LayoutParams getAnimLp(ActivityRecord activity) {
    final WindowState mainWindow = activity != null ? activity.findMainWindow() : null;
    return mainWindow != null ? mainWindow.mAttrs : null;
}

说明: 获取 Activity 主窗口的 LayoutParams。


步骤 10: 应用动画

10.1 AppTransitionController.applyAnimations(ArraySet, ...)

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 1095-1145 行
private void applyAnimations(ArraySet<ActivityRecord> openingApps,
        ArraySet<ActivityRecord> closingApps, @TransitionOldType int transit,
        LayoutParams animLp, boolean voiceInteraction) {
    final RecentsAnimationController rac = mService.getRecentsAnimationController();
    if (transit == WindowManager.TRANSIT_OLD_UNSET
            || (openingApps.isEmpty() && closingApps.isEmpty())) {
        if (rac != null) {
            rac.sendTasksAppeared();
        }
        return;
    }

    // ... 省略 letterbox 动画处理 ...

    // ★★★ 关键调用:获取打开动画目标 ★★★
    final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
            openingApps, closingApps, true /* visible */);
    // ★★★ 关键调用:获取关闭动画目标 ★★★
    final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
            openingApps, closingApps, false /* visible */);
    
    // ★★★ 关键调用:为打开的 App 应用动画 ★★★
    applyAnimations(openingWcs, openingApps, transit, true /* visible */, animLp,
            voiceInteraction);
    // ★★★ 关键调用:为关闭的 App 应用动画 ★★★
    applyAnimations(closingWcs, closingApps, transit, false /* visible */, animLp,
            voiceInteraction);
    
    // ... 省略清理代码 ...
}

说明:

  • 调用 getAnimationTargets() 获取需要动画的 WindowContainer
  • 分别为打开和关闭的 App 调用 applyAnimations()

10.2 AppTransitionController.applyAnimations(ArraySet, ...)

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 909-927 行
private void applyAnimations(ArraySet<WindowContainer> wcs, ArraySet<ActivityRecord> apps,
        @TransitionOldType int transit, boolean visible, LayoutParams animLp,
        boolean voiceInteraction) {
    final int wcsCount = wcs.size();
    for (int i = 0; i < wcsCount; i++) {
        final WindowContainer wc = wcs.valueAt(i);
        // 收集参与过渡的 ActivityRecord
        final ArrayList<ActivityRecord> transitioningDescendants = new ArrayList<>();
        for (int j = 0; j < apps.size(); ++j) {
            final ActivityRecord app = apps.valueAt(j);
            if (app.isDescendantOf(wc)) {
                transitioningDescendants.add(app);
            }
        }
        // ★★★ 关键调用:为 WindowContainer 应用动画 ★★★
        wc.applyAnimation(animLp, transit, visible, voiceInteraction, transitioningDescendants);
    }
}

说明: 遍历所有需要动画的 WindowContainer,调用其 applyAnimation() 方法。


步骤 11: WindowContainer.applyAnimation()

11.1 WindowContainer.applyAnimation(LayoutParams, int, boolean, boolean, ArrayList)

文件: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

// 第 3092-3120 行
boolean applyAnimation(WindowManager.LayoutParams lp, @TransitionOldType int transit,
        boolean enter, boolean isVoiceInteraction,
        @Nullable ArrayList<WindowContainer> sources) {
    if (mWmService.mDisableTransitionAnimation) {
        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                "applyAnimation: transition animation is disabled or skipped. "
                        + "container=%s", this);
        cancelAnimation();
        return false;
    }

    try {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation");
        if (okToAnimate()) {
            ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
                    "applyAnimation: transit=%s, enter=%b, wc=%s",
                    AppTransition.appTransitionOldToString(transit), enter, this);
            // ★★★ 关键调用:调用 applyAnimationUnchecked ★★★
            applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
        } else {
            cancelAnimation();
        }
    } finally {
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }

    return isAnimating();
}

说明:

  • 检查是否禁用过渡动画
  • 检查是否可以动画 (okToAnimate())
  • 调用 applyAnimationUnchecked() 实际应用动画

11.2 WindowContainer.applyAnimationUnchecked()

文件: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

// 第 3253-3280 行
protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter,
        @TransitionOldType int transit, boolean isVoiceInteraction,
        @Nullable ArrayList<WindowContainer> sources) {
    final Task task = asTask();
    if (task != null && !enter && !task.isActivityTypeHomeOrRecents()) {
        // ... 处理 IME 截图 ...
    }
    
    // ★★★ 关键调用:获取动画适配器 ★★★
    final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
            transit, enter, isVoiceInteraction);
    AnimationAdapter adapter = adapters.first;
    AnimationAdapter thumbnailAdapter = adapters.second;
    
    // ... 应用动画适配器 ...
}

说明: 调用 getAnimationAdapter() 获取动画适配器。


11.3 WindowContainer.getAnimationAdapter()

文件: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

// 第 3097-3250 行
Pair<AnimationAdapter, AnimationAdapter> getAnimationAdapter(WindowManager.LayoutParams lp,
        @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction) {
    // ... 省略远程动画处理 ...

    // ★★★ 关键调用:加载本地动画 ★★★
    mNeedsAnimationBoundsLayer = (appRootTaskClipMode == ROOT_TASK_CLIP_AFTER_ANIM);
    final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);

    if (a != null) {
        final float windowCornerRadius = !inMultiWindowMode()
                ? getDisplayContent().getWindowCornerRadius()
                : 0;
        // ... 省略部分代码 ...
        
        // ★★★ 创建动画适配器 ★★★
        AnimationAdapter adapter = new LocalAnimationAdapter(
                new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
                        getDisplayContent().mAppTransition.canSkipFirstFrame(),
                        appRootTaskClipMode, true /* isAppAnimation */, windowCornerRadius),
                getSurfaceAnimationRunner());

        resultAdapters = new Pair<>(adapter, null);
        mNeedsZBoost = a.getZAdjustment() == Animation.ZORDER_TOP
                || AppTransition.isClosingTransitOld(transit);
        mTransit = transit;
        mTransitFlags = getDisplayContent().mAppTransition.getTransitFlags();
    } else {
        resultAdapters = new Pair<>(null, null);
    }
    return resultAdapters;
}

说明:

  • 调用 loadAnimation() 加载动画对象
  • 创建 LocalAnimationAdapter 包装动画
  • 创建 WindowAnimationSpec 包含动画规格

11.4 WindowContainer.loadAnimation()

文件: frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

// 第 3355-3410 行
private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
                                boolean isVoiceInteraction) {
    if (AppTransitionController.isTaskViewTask(this) || (isOrganized()
            && getWindowingMode() != WINDOWING_MODE_FULLSCREEN
            && getWindowingMode() != WINDOWING_MODE_FREEFORM
            && getWindowingMode() != WINDOWING_MODE_MULTI_WINDOW)) {
        return null;
    }

    final DisplayContent displayContent = getDisplayContent();
    final DisplayInfo displayInfo = displayContent.getDisplayInfo();
    final int width = displayInfo.appWidth;
    final int height = displayInfo.appHeight;
    ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: container=%s", this);

    // 计算动画帧
    final Rect frame = new Rect(0, 0, width, height);
    final Rect displayFrame = new Rect(0, 0,
            displayInfo.logicalWidth, displayInfo.logicalHeight);
    final Rect insets = new Rect();
    final Rect stableInsets = new Rect();
    final Rect surfaceInsets = new Rect();
    getAnimationFrames(frame, insets, stableInsets, surfaceInsets);

    if (mLaunchTaskBehind) {
        enter = false;
    }
    ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
            "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s "
                    + "surfaceInsets=%s",
            AppTransition.appTransitionOldToString(transit), enter, frame, insets,
            surfaceInsets);
    final Configuration displayConfig = displayContent.getConfiguration();
    
    // ★★★ 关键调用:通过 AppTransition 加载动画 ★★★
    final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
            displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
            surfaceInsets, stableInsets, isVoiceInteraction, inFreeformWindowingMode(), this);
    
    if (a != null) {
        // 设置最大动画时长
        a.restrictDuration(MAX_APP_TRANSITION_DURATION);
        // ... 省略部分代码 ...
        final int containingWidth = frame.width();
        final int containingHeight = frame.height();
        a.initialize(containingWidth, containingHeight, width, height);
        a.scaleCurrentDuration(mWmService.getTransitionAnimationScaleLocked());
    }
    return a;
}

说明:

  • 调用 getAnimationFrames() 获取动画帧信息
  • 调用 AppTransition.loadAnimation() 加载动画对象

步骤 12: AppTransition.loadAnimation() 加载动画

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

// 第 764-920 行
@Nullable
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) {

    final boolean canCustomizeAppTransition = container.canCustomizeAppTransition();

    // ... 省略各种特殊过渡类型的处理 ...

    // ★★★ 关键:默认动画加载路径 ★★★
    } else {
        // ★★★ 关键调用:映射过渡类型到动画属性 ★★★
        int animAttr = mapOpenCloseTransitTypes(transit, enter);
        if (animAttr != 0) {
            final CustomAppTransition customAppTransition =
                    getCustomAppTransition(animAttr, container);
            if (customAppTransition != null) {
                a = loadCustomActivityAnimation(customAppTransition, enter, container);
            } else {
                if (canCustomizeAppTransition) {
                    a = loadAnimationAttr(lp, animAttr, transit);
                } else {
                    // ★★★ 关键调用:加载默认动画 ★★★
                    a = mTransitionAnimation.loadDefaultAnimationAttr(animAttr, transit);
                }
            }
        } else {
            a = null;
        }

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

    return a;
}

说明:

  • 调用 mapOpenCloseTransitTypes() 将过渡类型映射到动画属性
  • 调用 loadDefaultAnimationAttr() 加载默认动画

步骤 13: AppTransition.mapOpenCloseTransitTypes() 映射动画属性

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

// 第 552-640 行
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;
        case TRANSIT_OLD_ACTIVITY_CLOSE:
        case TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE:
            // ★★★ 关键:Activity 关闭的动画属性映射 ★★★
            animAttr = enter
                    ? WindowAnimation_activityCloseEnterAnimation   // 下方 Activity 进入
                    : WindowAnimation_activityCloseExitAnimation;   // 关闭 Activity 退出
            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;
        // ... 其他过渡类型 ...
    }

    return animAttr;
}

说明:

  • 对于 TRANSIT_OLD_ACTIVITY_CLOSE 过渡类型:
    • enter=true(下方露出的 Activity)时,返回 WindowAnimation_activityCloseEnterAnimation
    • enter=false(关闭的 Activity)时,返回 WindowAnimation_activityCloseExitAnimation

步骤 14: TransitionAnimation.loadDefaultAnimationAttr() 加载默认动画

14.1 TransitionAnimation.loadDefaultAnimationAttr(int, int)

文件: frameworks/base/core/java/com/android/internal/policy/TransitionAnimation.java

// 第 350-353 行
/** Load animation by attribute Id from android package. */
@Nullable
public Animation loadDefaultAnimationAttr(int animAttr, @TransitionOldType int transit) {
    return loadAnimationAttr(DEFAULT_PACKAGE, mDefaultWindowAnimationStyleResId, animAttr,
            false /* translucent */, transit);
}

说明:

  • DEFAULT_PACKAGE = "android" - 表示从系统资源加载
  • mDefaultWindowAnimationStyleResId - 系统主题中定义的 windowAnimationStyle 样式资源 ID

14.2 TransitionAnimation.loadAnimationAttr(String, int, int, boolean, int)

文件: frameworks/base/core/java/com/android/internal/policy/TransitionAnimation.java

// 第 348-360 行
@Nullable
public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr,
        boolean translucent, @TransitionOldType int transit) {
    // 从 AttributeCache 获取动画样式
    final AttributeCache.Entry ent = getCachedAnimations(packageName, animStyleResId);
    if (ent != null) {
        // 从样式中读取动画资源 ID
        final int resId = ent.array.getResourceId(animAttr, 0);
        if (resId != 0) {
            // 加载动画资源
            return loadAnimationSafely(mContext, resId, mTag);
        }
    }
    return null;
}

说明:

  • 调用 getCachedAnimations() 从缓存中获取动画样式
  • 从样式中读取动画资源 ID
  • 调用 loadAnimationSafely() 加载动画资源

步骤 15: AppTransitionController.handleClosingApps() 提交关闭可见性

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 1201-1225 行
private void handleClosingApps() {
    final ArraySet<ActivityRecord> closingApps = mDisplayContent.mClosingApps;
    final int appsCount = closingApps.size();

    for (int i = 0; i < appsCount; i++) {
        final ActivityRecord app = closingApps.valueAt(i);
        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now closing app %s", app);

        // ★★★ 关键调用:提交可见性变化为 false ★★★
        app.commitVisibility(false /* visible */, false /* performLayout */);
        // ★★★ 关键调用:更新报告的可见性 ★★★
        app.updateReportedVisibilityLocked();
        // 强制设置 allDrawn 标志,确保动画能启动
        app.allDrawn = true;
        // ★★★ 关键调用:移除 starting window ★★★
        if (app.mStartingWindow != null && !app.mStartingWindow.mAnimatingExit) {
            app.removeStartingWindow();
        }

        if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailDown()) {
            app.attachThumbnailAnimation();
        }
    }
}

说明:

  • 遍历 mClosingApps 中的所有 Activity
  • 调用 commitVisibility(false, false) 提交可见性变化
  • 调用 updateReportedVisibilityLocked() 更新报告的可见性
  • 调用 removeStartingWindow() 清理 starting window

步骤 16: AppTransitionController.handleOpeningApps() 提交打开可见性

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 1158-1200 行
private void handleOpeningApps() {
    final ArraySet<ActivityRecord> openingApps = mDisplayContent.mOpeningApps;
    final int appsCount = openingApps.size();

    for (int i = 0; i < appsCount; i++) {
        final ActivityRecord app = openingApps.valueAt(i);
        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now opening app %s", app);

        // ★★★ 关键调用:提交可见性变化为 true ★★★
        app.commitVisibility(true /* visible */, false /* performLayout */);

        // ... 省略部分代码 ...

        // ★★★ 关键调用:更新报告的可见性 ★★★
        app.updateReportedVisibilityLocked();
        app.waitingToShow = false;
        if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                ">>> OPEN TRANSACTION handleAppTransitionReady()");
        mService.openSurfaceTransaction();
        try {
            // ★★★ 关键调用:显示所有窗口 ★★★
            app.showAllWindowsLocked();
        } finally {
            mService.closeSurfaceTransaction("handleAppTransitionReady");
            if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG,
                    "<<< CLOSE TRANSACTION handleAppTransitionReady()");
        }

        if (mDisplayContent.mAppTransition.isNextAppTransitionThumbnailUp()) {
            app.attachThumbnailAnimation();
        } else if (mDisplayContent.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) {
            app.attachCrossProfileAppsThumbnailAnimation();
        }
    }
}

说明:

  • 遍历 mOpeningApps 中的所有 Activity
  • 调用 commitVisibility(true, false) 提交可见性变化
  • 调用 showAllWindowsLocked() 显示所有窗口

步骤 17: AppTransitionController.handleChangingApps() 处理变化的容器

文件: frameworks/base/services/core/java/com/android/server/wm/AppTransitionController.java

// 第 1245-1255 行
private void handleChangingApps(@TransitionOldType int transit) {
    final ArraySet<WindowContainer> apps = mDisplayContent.mChangingContainers;
    final int appsCount = apps.size();
    for (int i = 0; i < appsCount; i++) {
        WindowContainer wc = apps.valueAt(i);
        ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Now changing app %s", wc);
        // ★★★ 关键调用:应用变化动画 ★★★
        wc.applyAnimation(null, transit, true, false, null /* sources */);
    }
}

三、默认关闭动画资源定义

3.1 动画样式定义

文件: frameworks/base/core-res/res/values/styles.xml

<!-- 第 96-130 行左右 -->
<style name="Animation.Activity">
    <!-- Activity 打开动画 -->
    <item name="activityOpenEnterAnimation">@anim/activity_open_enter</item>
    <item name="activityOpenExitAnimation">@anim/activity_open_exit</item>
    
    <!-- ★★★ Activity 关闭动画 ★★★ -->
    <item name="activityCloseEnterAnimation">@anim/activity_close_enter</item>
    <item name="activityCloseExitAnimation">@anim/activity_close_exit</item>
    
    <!-- Task 打开/关闭动画 -->
    <item name="taskOpenEnterAnimation">@anim/task_open_enter</item>
    <item name="taskOpenExitAnimation">@anim/task_open_exit</item>
    <item name="taskCloseEnterAnimation">@anim/task_close_enter</item>
    <item name="taskCloseExitAnimation">@anim/task_close_exit</item>
    
    <!-- 其他动画属性... -->
</style>

说明:

  • activityCloseEnterAnimation: 下方 Activity 进入动画(当顶部 Activity 关闭时)
  • activityCloseExitAnimation: 关闭 Activity 的退出动画

3.2 关闭退出动画 (activity_close_exit.xml)

文件: frameworks/base/core-res/res/anim/activity_close_exit.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false">
    <!-- 透明度动画:从完全不透明渐变到完全透明 -->
    <alpha 
        android:fromAlpha="1.0" 
        android:toAlpha="0.0"
        android:startOffset="35" 
        android:duration="83"
        android:interpolator="@interpolator/linear" />
    
    <!-- 平移动画:向右平移 96dp -->
    <translate 
        android:fromXDelta="0" 
        android:toXDelta="96dp"
        android:duration="450"
        android:interpolator="@interpolator/fast_out_extra_slow_in" />
    
    <!-- 延伸动画:左侧延伸 96dp -->
    <extend 
        android:fromExtendLeft="96dp" 
        android:toExtendLeft="0"
        android:duration="450" />
</set>

动画效果:

  • 透明度从 1.0 渐变到 0.0(先延迟 35ms,然后 83ms 完成)
  • 同时向右平移 96dp(450ms 完成)
  • 使用 fast_out_extra_slow_in 插值器

3.3 关闭进入动画 (activity_close_enter.xml)

文件: frameworks/base/core-res/res/anim/activity_close_enter.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false">
    <!-- 透明度动画:保持完全不透明 -->
    <alpha 
        android:fromAlpha="1.0" 
        android:toAlpha="1.0"
        android:duration="450" />
    
    <!-- 平移动画:从左侧 -96dp 平移到原位置 -->
    <translate 
        android:fromXDelta="-96dp" 
        android:toXDelta="0"
        android:duration="450"
        android:interpolator="@interpolator/fast_out_extra_slow_in" />
    
    <!-- 延伸动画:右侧延伸 96dp -->
    <extend 
        android:fromExtendRight="96dp" 
        android:toExtendRight="0"
        android:duration="450" />
</set>

动画效果:

  • 透明度保持 1.0(完全不透明)
  • 从左侧 -96dp 位置平移到原位置(450ms 完成)
  • 使用 fast_out_extra_slow_in 插值器

四、关键数据结构

4.1 Transition 类型常量

新版本 Transition 类型 (声明时使用):

常量说明
TRANSIT_NONE0无过渡
TRANSIT_OPEN1打开
TRANSIT_CLOSE2关闭 ← Activity 关闭使用
TRANSIT_TO_FRONT3到前台
TRANSIT_TO_BACK4到后台
TRANSIT_RELAUNCH5重新启动
TRANSIT_CHANGE6变更

旧版本 Transition 类型 (加载动画时使用):

常量说明
TRANSIT_OLD_ACTIVITY_OPEN6Activity 打开
TRANSIT_OLD_ACTIVITY_CLOSE7Activity 关闭 ← 关闭动画匹配
TRANSIT_OLD_TASK_OPEN8Task 打开
TRANSIT_OLD_TASK_CLOSE9Task 关闭
TRANSIT_OLD_TASK_TO_FRONT10Task 到前台
TRANSIT_OLD_TASK_TO_BACK11Task 到后台

4.2 动画属性常量

定义在 com.android.internal.R.styleable.WindowAnimation:

常量说明
activityOpenEnterAnimationActivity 打开进入动画
activityOpenExitAnimationActivity 打开退出动画
activityCloseEnterAnimationActivity 关闭进入动画(下方 Activity)
activityCloseExitAnimationActivity 关闭退出动画(关闭的 Activity)
taskOpenEnterAnimationTask 打开进入动画
taskOpenExitAnimationTask 打开退出动画
taskCloseEnterAnimationTask 关闭进入动画
taskCloseExitAnimationTask 关闭退出动画

五、关键代码位置总结表

步骤文件方法名行号作用
1Activity.javafinish()7018App 进程发起 finish 请求
2Activity.javafinish(int)7005调用 ActivityClient
3ActivityClient.javafinishActivity()172Binder IPC 调用
4ActivityClientController.javafinishActivity()424System Server 处理 finish
5ActivityRecord.javafinishIfPossible(int, Intent, NeededUriGrants, String, boolean)3496准备关闭过渡
6ActivityRecord.javamakeFinishingLocked()3526标记为 finishing
7DisplayContent.javaprepareAppTransition(int)5608声明关闭过渡入口
8DisplayContent.javaprepareAppTransition(int, int)5615调用 AppTransition
9AppTransition.javaprepareAppTransition(int, int)1480添加过渡请求
10ActivityRecord.javasetVisibility(boolean)3584设置不可见入口
11ActivityRecord.javasetVisibility(boolean, boolean)5307内部实现
12ActivityRecord.javadeferCommitVisibilityChange(boolean)5455决定延迟提交
13ActivityRecord.javamClosingApps.add(this)5497加入关闭列表
14DisplayContent.javaexecuteAppTransition()5634执行过渡
15AppTransitionController.javahandleAppTransitionReady()172处理过渡入口
16AppTransitionController.javatransitionGoodToGo()174检查是否准备好
17AppTransitionController.javagetTransitCompatType()263确定过渡类型
18AppTransitionController.javagetAnimationTargets()475获取动画目标
19AppTransitionController.javagetTransitContainerType()547获取容器类型
20AppTransitionController.java返回 TRANSIT_OLD_ACTIVITY_CLOSE530Activity 关闭类型
21AppTransitionController.javafindAnimLayoutParamsToken()279获取动画参数
22AppTransitionController.javagetAnimLp()560获取 LayoutParams
23AppTransitionController.javaapplyAnimations(ArraySet<ActivityRecord>, ...)300应用动画入口
24AppTransitionController.javaapplyAnimations(ArraySet<WindowContainer>, ...)909遍历容器应用
25WindowContainer.javaapplyAnimation(...)3092应用动画
26WindowContainer.javaapplyAnimationUnchecked(...)3253应用动画内部
27WindowContainer.javagetAnimationAdapter(...)3097获取动画适配器
28WindowContainer.javaloadAnimation(...)3355加载动画
29AppTransition.javaloadAnimation(...)764加载动画
30AppTransition.javamapOpenCloseTransitTypes(int, boolean)562映射动画属性
31TransitionAnimation.javaloadDefaultAnimationAttr(int, int)351加载默认动画
32TransitionAnimation.javaloadAnimationAttr(...)348加载动画资源
33AppTransitionController.javahandleClosingApps()1201处理关闭 App
34ActivityRecord.javacommitVisibility(boolean, boolean)1208提交可见性
35AppTransitionController.javahandleOpeningApps()1158处理打开 App
36ActivityRecord.javashowAllWindowsLocked()1180显示所有窗口

六、总结

6.1 核心流程

  1. finish 请求发起: App 进程调用 Activity.finish()ActivityClient.finishActivity() (Binder IPC)
  2. 声明关闭过渡: ActivityRecord.finishIfPossible()DisplayContent.prepareAppTransition(TRANSIT_CLOSE)AppTransition.prepareAppTransition()
  3. 加入关闭列表: ActivityRecord.setVisibility(false)setVisibility(boolean, boolean)deferCommitVisibilityChange(false)mClosingApps.add(this)
  4. 确定过渡类型: handleAppTransitionReady()getTransitCompatType()getAnimationTargets()getTransitContainerType() → 返回 TRANSIT_OLD_ACTIVITY_CLOSE
  5. 应用动画: applyAnimations()WindowContainer.applyAnimation()applyAnimationUnchecked()getAnimationAdapter()loadAnimation()
  6. 加载动画资源: AppTransition.loadAnimation()mapOpenCloseTransitTypes()TransitionAnimation.loadDefaultAnimationAttr() → 从 Animation.Activity 样式加载

6.2 关键判断点

  • Shell Transition 是否启用: 在 AppTransition.prepareAppTransition() 中检查
  • Activity 状态: RESUMED 状态会先 pause,非 RESUMED 状态直接执行过渡
  • 容器类型判断: getTransitContainerType() 返回 TYPE_ACTIVITY 则使用 Activity 关闭动画

6.3 默认动画效果

  • 关闭 Activity 退出动画: 向右平移 96dp + 透明度渐变,持续 450ms
  • 下方 Activity 进入动画: 从左侧 -96dp 平移进入,持续 450ms