AOSP15 WMS/AMS系统开发 - 远程动画 (ShellAnimation) 源码深度分析

基于Android 15 (AOSP)源码,完整梳理远程动画的注册、收集、请求、启动、执行、完成与清理全流程。

本文以 Shell Transitions 为主要分析路径(Android 15 默认启用),Legacy App Transition 作为对比简要保留。两者共享 Leash/SurfaceControl 底层机制。


目录

  1. 概述
  2. [Shell Transitions vs Legacy App Transition 对比](#Shell Transitions vs Legacy App Transition 对比 "#2-shell-transitions-vs-legacy-app-transition-%E5%AF%B9%E6%AF%94")
  3. 核心类关系图
  4. [阶段一:TransitionPlayer 注册](#阶段一:TransitionPlayer 注册 "#4-%E9%98%B6%E6%AE%B5%E4%B8%80transitionplayer-%E6%B3%A8%E5%86%8C")
  5. [阶段二:Transition 创建与收集](#阶段二:Transition 创建与收集 "#5-%E9%98%B6%E6%AE%B5%E4%BA%8Ctransition-%E5%88%9B%E5%BB%BA%E4%B8%8E%E6%94%B6%E9%9B%86")
  6. [阶段三:请求 Shell 启动 (requestStartTransition)](#阶段三:请求 Shell 启动 (requestStartTransition) "#6-%E9%98%B6%E6%AE%B5%E4%B8%89%E8%AF%B7%E6%B1%82-shell-%E5%90%AF%E5%8A%A8-requeststarttransition")
  7. [阶段四:Shell 确认启动与 Leash 构建](#阶段四:Shell 确认启动与 Leash 构建 "#7-%E9%98%B6%E6%AE%B5%E5%9B%9Bshell-%E7%A1%AE%E8%AE%A4%E5%90%AF%E5%8A%A8%E4%B8%8E-leash-%E6%9E%84%E5%BB%BA")
  8. [阶段五:onTransitionReady --- 动画委托给 Shell](#阶段五:onTransitionReady — 动画委托给 Shell "#8-%E9%98%B6%E6%AE%B5%E4%BA%94ontransitionready--%E5%8A%A8%E7%94%BB%E5%A7%94%E6%89%98%E7%BB%99-shell")
  9. 阶段六:远程进程执行动画
  10. [阶段七:finishTransition --- 完成与清理](#阶段七:finishTransition — 完成与清理 "#10-%E9%98%B6%E6%AE%B5%E4%B8%83finishtransition--%E5%AE%8C%E6%88%90%E4%B8%8E%E6%B8%85%E7%90%86")
  11. 异常与取消处理
  12. 关键数据结构
  13. 完整时序图

1. 概述

1.1 什么是 Shell Transitions 远程动画

Android 12 引入、Android 15 全面启用的 Shell Transitions 体系,将窗口转场动画的编排权 从 system_server 转移到 Shell 进程 (Launcher/SystemUI)。WMS 只负责收集窗口变化,Shell 负责决定如何播放动画------可以自己播,也可以通过 RemoteTransition 委托给第三方进程。

1.2 核心设计思想

  • WMS-Core/Shell 分离:Core(system_server)管理窗口状态、收集参与者;Shell 决定动画策略、播放动画
  • Leash 机制 :为每个参与转场的 WindowContainer 创建 SurfaceControl Leash,Shell/远程进程通过操作 Leash 实现动画
  • BLASTSync 同步 :通过 BLASTSyncEngine 确保所有参与者在动画开始前完成首帧绘制
  • 并行 Transition:支持多个 Transition 同时在不同 track 上播放

1.3 四阶段模型

sql 复制代码
Request → Collect → Play → Finish
  ↓         ↓        ↓       ↓
Shell决定   Core收集   Shell/远程播放  Core清理

2. Shell Transitions vs Legacy App Transition 对比

2.1 分歧点源码

两个系统在同一处代码产生分支------DisplayContent.java

java 复制代码
if (mTransitionController.isShellTransitionsEnabled()) {
    // Shell 路径:检查是否正在收集
    if (!mTransitionController.isCollecting(r)) return false;
} else {
    // Legacy 路径:检查 AppTransition 是否就绪
    if (!mAppTransition.isTransitionSet() || !mOpeningApps.contains(r)) return false;
}

isShellTransitionsEnabled() 实现(TransitionController.java):

java 复制代码
boolean isShellTransitionsEnabled() {
    return !mTransitionPlayers.isEmpty();  // 只要 Shell 注册了 Player 就启用
}

当 Shell 启用时,Legacy 的 AppTransition.prepareAppTransition() 直接短路AppTransition.java):

java 复制代码
boolean prepareAppTransition(@TransitionType int transit, @TransitionFlags int flags) {
    if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
        return false;  // ← Legacy 完全跳过
    }
    // Legacy 逻辑...
}

2.2 API 对照

维度 Shell Transitions Legacy App Transition
注册接口 WindowOrganizer.registerTransitionPlayer(ITransitionPlayer) AppTransition.overridePendingAppTransitionRemote()
触发端 TransitionController 收集变更后通知 Shell AppTransitionController.handleAppTransitionReady()
动画接口 ITransitionPlayer.onTransitionReady() IRemoteAnimationRunner.onAnimationStart()
远程委托 IRemoteTransition.startAnimation() IRemoteAnimationRunner.onAnimationStart()
动画目标 TransitionInfo(层级结构) RemoteAnimationTarget[](扁平数组)
完成回调 IWindowOrganizerController.finishTransition() IRemoteAnimationFinishedCallback.onAnimationFinished()
超时机制 BLASTSync + Transition 自身超时 AppTransition.TIMEOUT_MS = 10000
并行支持 多 Track 并行 单一 Transition
启用条件 Shell 注册 ITransitionPlayer 默认

2.3 调用链对比

Shell Transitions(Launcher 启动应用)

scss 复制代码
ActivityStarter.execute()
  → TransitionController.collect(activity)                    // 收集
  → TransitionController.requestStartTransition()             // 请求
      → ITransitionPlayer.requestStartTransition(token, req)  // Binder → Shell
  → [Shell 调用 startTransition]
  → Transition.start() → 构建 TransitionInfo + Leash
  → ITransitionPlayer.onTransitionReady(token, info, t, finishT) // Binder → Shell
  → [Shell 播放动画 或 委托 IRemoteTransition]
  → IWindowOrganizerController.finishTransition(token, wct)   // Binder → Core
  → TransitionController.finishTransition() → Transition.finishTransition()

Legacy App Transition(对比)

scss 复制代码
RootWindowContainer.checkAppTransitionReady()
  → AppTransitionController.handleAppTransitionReady()
      → AppTransition.goodToGo()
          → RemoteAnimationController.goodToGo()
              → IRemoteAnimationRunner.onAnimationStart()     // Binder → 远程
  → IRemoteAnimationFinishedCallback.onAnimationFinished()    // Binder → Core
      → RemoteAnimationController.onAnimationFinished()

3. 核心类关系图

yaml 复制代码
┌───────────────────────────────────────────────────────────────────────┐
│                        TransitionController                           │
│                                                                       │
│  mTransitionPlayers: ArrayList<TransitionPlayerRecord>                │
│       └─ ITransitionPlayer (Shell 注册)                                │
│                                                                       │
│  mCollectingTransition: Transition    ← 当前正在收集的 Transition       │
│  mPlayingTransitions: ArrayList<Transition>  ← 正在播放的列表           │
│                                                                       │
│  createTransition() → new Transition(type, flags, this, syncEngine)   │
│  requestStartTransition() → ITransitionPlayer.requestStartTransition()│
│  finishTransition() → Transition.finishTransition()                   │
└────────────────────────┬──────────────────────────────────────────────┘
                         │ 创建/管理
                         ▼
┌──────────────────────────────────────────────────────────────────────┐
│                          Transition                                  │
│                                                                      │
│  mType: int (TRANSIT_OPEN/CLOSE/CHANGE...)                           │
│  mSyncId: int (BLASTSyncEngine ID)                                   │
│  mState: STATE_COLLECTING → STARTED → PLAYING → FINISHED             │
│                                                                      │
│  mParticipants: ArraySet<WindowContainer>  ← 参与者集合                │
│  mChanges: ArrayMap<WindowContainer, ChangeInfo>  ← 变更记录          │
│  mTargets: ArrayList<ChangeInfo>  ← 最终动画目标(提升后)               │
│                                                                      │
│  mStartTransaction: Transaction   ← 动画前 Surface 状态                │
│  mFinishTransaction: Transaction  ← 动画后 Surface 归位                │
│                                                                      │
│  collect(wc) → 收集参与者                                              │
│  start() → 构建动画信息                                                │
│  finishTransition() → 清理归位                                        │
└──────────────────────────────────────────────────────────────────────┘
                         │ Binder IPC
                         ▼
┌──────────────────────────────────────────────────────────────────────┐
│              ITransitionPlayer (Shell 进程实现)                       │
│                                                                      │
│  requestStartTransition(token, TransitionRequestInfo)                │
│      → Shell 决定如何处理(可委托 RemoteTransition)                     │
│                                                                      │
│  onTransitionReady(token, TransitionInfo, startT, finishT)           │
│      → 收到完整变化信息,开始播放动画                                     │
│                                                                      │
│  ┌─ TransitionInfo ─────────────────────────────────────┐            │
│  │  type, flags                                         │            │
│  │  changes: List<Change> (层级结构)                     │            │
│  │    └─ each Change:                                   │            │
│  │       leash: SurfaceControl                          │            │
│  │       mode: OPEN/CLOSE/CHANGE...                     │            │
│  │       startAbsBounds, endAbsBounds                   │            │
│  │       snapshot: SurfaceControl (起始快照)             │            │
│  │  roots: List<Root> (每 Display 一个根 Leash)          │            │
│  └──────────────────────────────────────────────────────┘            │
│                                                                      │
│  ┌─ 可委托 IRemoteTransition ────────────────────────────┐            │
│  │  startAnimation(token, info, t, finishCallback)      │            │
│  │  mergeAnimation(token, info, t, mergeTarget, cb)     │            │
│  │  onTransitionConsumed(token, aborted)                │            │
│  └──────────────────────────────────────────────────────┘            │
└──────────────────────────────────────────────────────────────────────┘

4. 阶段一:TransitionPlayer 注册

Shell(通常为 SystemUI)在启动时通过 WindowOrganizer.registerTransitionPlayer() 注册。

4.1 公共 API 入口

源码 : WindowOrganizer.java

java 复制代码
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
public void registerTransitionPlayer(@NonNull ITransitionPlayer player) {
    try {
        getWindowOrganizerController().registerTransitionPlayer(player);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

4.2 WindowOrganizerController 注册

源码 : WindowOrganizerController.java

java 复制代码
public void registerTransitionPlayer(ITransitionPlayer player) {
    enforceTaskPermission("registerTransitionPlayer()");
    final int callerPid = Binder.getCallingPid();
    final int callerUid = Binder.getCallingUid();
    final long ident = Binder.clearCallingIdentity();
    try {
        synchronized (mGlobalLock) {
            final WindowProcessController wpc =
                    mService.getProcessController(callerPid, callerUid);
            mTransitionController.registerTransitionPlayer(player, wpc);
        }
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

4.3 TransitionController 注册

源码 : TransitionController.java

java 复制代码
void registerTransitionPlayer(@Nullable ITransitionPlayer player,
        @Nullable WindowProcessController playerProc) {
    if (!mTransitionPlayers.isEmpty()) {
        flushRunningTransitions();  // 已有 Player,先刷完正在运行的
    }
    mTransitionPlayers.add(new TransitionPlayerRecord(player, playerProc));
}

注册完成后,isShellTransitionsEnabled() 返回 true,所有窗口转场走 Shell 路径。


5. 阶段二:Transition 创建与收集

以 Launcher 启动应用为例,追踪完整流程。

5.1 Transition 创建 --- createAndStartCollecting

源码 : ActivityStarter.java

Transition 在 startActivityUnchecked 之前就创建了:

java 复制代码
// ActivityStarter 中,execute() → startActivityUnchecked() 调用链
final Transition newTransition = r.mTransitionController.isShellTransitionsEnabled()
        ? r.mTransitionController.createAndStartCollecting(TRANSIT_OPEN) : null;
final boolean isIndependent = newTransition != null;
final Transition transition = isIndependent ? newTransition
        : mService.getTransitionController().getCollectingTransition();

源码 : TransitionController.java

createAndStartCollecting 封装了 Transition 创建和收集启动:

java 复制代码
Transition createAndStartCollecting(int type) {
    if (mTransitionPlayers.isEmpty()) return null;
    // ... 并行/队列检查 ...
    Transition transit = new Transition(type, 0 /* flags */, this, mSyncEngine);
    moveToCollecting(transit);  // 设为 mCollectingTransition,开始收集
    return transit;
}

5.2 取出 RemoteTransition

源码 : ActivityStarter.java

execute() 方法内部(内层 startActivityInner 的外层):

java 复制代码
RemoteTransition remoteTransition = r.takeRemoteTransition();

RemoteTransition(非 RemoteAnimationAdapter)是 Shell 路径的远程动画载体,包含 IRemoteTransitionIApplicationThread

5.3 收集参与者 --- collect

源码 : ActivityStarter.java

同样在 execute() 方法内部,调用 startActivityInner 之前收集:

java 复制代码
mService.deferWindowLayout();
r.mTransitionController.collect(r);  // ★ 收集启动的 Activity
try {
    result = startActivityInner(r, sourceRecord, ...);
} finally {
    startedActivityRootTask = handleStartResult(r, options, result,
            isIndependentLaunch, remoteTransition, transition);
}

衔接说明createAndStartCollecting (5.1) 创建 Transition 并进入 COLLECTING 状态 → collect (5.3) 将 Activity 加入参与者集合 → handleStartResult 中调用 requestStartTransition(进入阶段6)

5.4 Transition.collect --- 收集实现

源码 : Transition.java

java 复制代码
void collect(@NonNull WindowContainer wc) {
    if (mState < STATE_COLLECTING) {
        throw new IllegalStateException("Transition hasn't started collecting.");
    }
    if (!isCollecting()) return;

    snapshotStartState(getAnimatableParent(wc));  // 快照起始状态
    if (mParticipants.contains(wc)) return;

    // 加入 BLASTSync 等待首帧绘制
    if (!isInTransientHide(wc)) {
        mSyncEngine.addToSyncSet(mSyncId, wc);
    }

    // 记录变更信息
    ChangeInfo info = mChanges.get(wc);
    if (info == null) {
        info = new ChangeInfo(wc);
        updateTransientFlags(info);
        mChanges.put(wc, info);
    }
    mParticipants.add(wc);
    recordDisplay(wc.getDisplayContent());

    if (info.mShowWallpaper) {
        wc.mDisplayContent.mWallpaperController.collectTopWallpapers(this);
    }
}

收集了什么

  • WindowContainer(Task、TaskFragment、Activity、WindowToken)
  • 父层级信息(用于后续目标提升)
  • Display 信息
  • 壁纸(如果需要)
  • 起始状态快照(bounds、visibility、rotation 等)

6. 阶段三:请求 Shell 启动 (requestStartTransition)

衔接 :阶段5 handleStartResult() 完成启动后,调用本阶段的 requestStartTransition() 通知 Shell。

6.1 ActivityStarter 发起请求

源码 : ActivityStarter.javahandleStartResult() 方法内)

java 复制代码
if (isIndependentLaunch && transition != null) {
    transitionController.requestStartTransition(transition,
            mTargetTask == null ? started.getTask() : mTargetTask,
            remoteTransition, null /* displayChange */);
}

6.2 TransitionController 构建请求

源码 : TransitionController.java

java 复制代码
Transition requestStartTransition(@NonNull Transition transition,
        @Nullable Task startTask,
        @Nullable RemoteTransition remoteTransition,
        @Nullable TransitionRequestInfo.DisplayChange displayChange) {
    // ...
    ActivityManager.RunningTaskInfo startTaskInfo = null;
    if (startTask != null) {
        startTaskInfo = startTask.getTaskInfo();
    }

    final TransitionRequestInfo request = new TransitionRequestInfo(transition.mType,
            startTaskInfo, pipChange, remoteTransition, displayChange,
            transition.getFlags(), transition.getSyncId());

    // ★ 通过 Binder 通知 Shell
    mTransitionPlayers.getLast().mPlayer.requestStartTransition(
            transition.getToken(), request);

    if (remoteTransition != null) {
        transition.setRemoteAnimationApp(remoteTransition.getAppThread());
    }
    return transition;
}

6.3 ITransitionPlayer.requestStartTransition

源码 : ITransitionPlayer.aidl

java 复制代码
void requestStartTransition(in IBinder transitionToken,
        in TransitionRequestInfo request);

Shell 收到后,可以:

  1. 立即调用 IWindowOrganizerController.startTransition() 确认开始
  2. 或者稍后确认(允许 Shell 做准备)
  3. 选择使用内置动画或 RemoteTransition 委托

7. 阶段四:Shell 确认启动与 Leash 构建

衔接 :阶段6 requestStartTransition() 通过 Binder 通知 Shell → Shell 调用 IWindowOrganizerController.startTransition(token, wct) 确认 → Core 调用 Transition.start() → BLASTSync 等待所有参与者首帧绘制完成 → 进入 PLAYING 状态构建 TransitionInfo 和 Leash → 通过 onTransitionReady() 传递给 Shell(阶段8)。

Shell 调用 startTransition() 后,Core 进入播放准备阶段。

7.1 Transition.start --- 状态转换

源码 : Transition.java

java 复制代码
void start() {
    if (mState < STATE_COLLECTING) {
        throw new IllegalStateException("Can't start Transition which isn't collecting.");
    }
    mState = STATE_STARTED;
    applyReady();
    mController.updateAnimatingState();
}

7.2 构建 TransitionInfo --- 目标提升与 Leash 创建

当所有参与者完成首帧绘制后,Transition 进入 PLAYING 状态,构建 TransitionInfo

getLeashSurface --- 获取/创建 LeashTransition.java):

java 复制代码
private static SurfaceControl getLeashSurface(WindowContainer wc,
        @Nullable SurfaceControl.Transaction t) {
    final DisplayContent asDC = wc.asDisplayContent();
    if (asDC != null) {
        return asDC.getWindowingLayer();
    }
    if (!wc.mTransitionController.useShellTransitionsRotation()) {
        final WindowToken asToken = wc.asWindowToken();
        if (asToken != null) {
            final SurfaceControl leash = t != null
                    ? asToken.getOrCreateFixedRotationLeash(t)
                    : asToken.getFixedRotationLeash();
            if (leash != null) return leash;
        }
    }
    return wc.getSurfaceControl();
}

calculateTransitionRoots --- 创建根 LeashTransition.java):

java 复制代码
static void calculateTransitionRoots(@NonNull TransitionInfo outInfo,
        ArrayList<ChangeInfo> sortedTargets,
        @NonNull SurfaceControl.Transaction startT) {
    for (int i = 0; i < sortedTargets.size(); ++i) {
        final WindowContainer<?> wc = sortedTargets.get(i).mContainer;
        if (isWallpaper(wc)) continue;
        final DisplayContent dc = wc.getDisplayContent();
        if (dc == null) continue;
        final int endDisplayId = dc.getDisplayId();

        if (outInfo.findRootIndex(endDisplayId) >= 0) continue;

        WindowContainer<?> ancestor = findCommonAncestor(sortedTargets, wc);
        WindowContainer leashReference = wc;
        while (leashReference.getParent() != ancestor) {
            leashReference = leashReference.getParent();
        }

        // ★ 创建根 Leash
        final SurfaceControl rootLeash = leashReference.makeAnimationLeash()
                .setName("Transition Root: " + leashReference.getName())
                .setCallsite("Transition.calculateTransitionRoots")
                .build();
        startT.setLayer(rootLeash, leashReference.getLastLayer());
        outInfo.addRootLeash(endDisplayId, rootLeash,
                ancestor.getBounds().left, ancestor.getBounds().top);
    }
}

每个 Change 的 Leash 构建Transition.java):

java 复制代码
final TransitionInfo.Change change = new TransitionInfo.Change(
        target.mRemoteToken != null
                ? target.mRemoteToken.toWindowContainerToken() : null,
        getLeashSurface(target, startT));

8. 阶段五:onTransitionReady --- 动画委托给 Shell

8.1 Core 调用 Shell

源码 : Transition.java

java 复制代码
// 先构建 finish/cleanup Transaction(无论是否有 Player 都需要)
buildFinishTransaction(mFinishTransaction, info, participantDisplays);
mCleanupTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
buildCleanupTransaction(mCleanupTransaction, info);

if (mController.getTransitionPlayer() != null && mIsPlayerEnabled) {
    mController.getTransitionPlayer().onTransitionReady(
            mToken, info, transaction, mFinishTransaction);
}

传递给 Shell 的四个参数:

参数 类型 说明
transitionToken IBinder 标识这个 Transition
info TransitionInfo 所有变化信息 + Leash
startT Transaction 动画前需要应用的 Surface 状态
finishT Transaction 动画后需要应用的归位操作

8.2 ITransitionPlayer.onTransitionReady

源码 : ITransitionPlayer.aidl

java 复制代码
void onTransitionReady(in IBinder transitionToken, in TransitionInfo info,
        in SurfaceControl.Transaction t, in SurfaceControl.Transaction finishT);

Shell 收到后:

  1. 应用 startTransaction(设置 Leash 的初始位置、可见性等)
  2. 播放动画(内置动画 或 委托 IRemoteTransition
  3. 动画完成后应用 finishTransaction(Surface 归位)
  4. 调用 finishTransition() 通知 Core

9. 阶段六:远程进程执行动画

9.1 Shell 委托给远程进程

TransitionRequestInfo 中包含 RemoteTransition 时,Shell 可以将动画委托给远程进程(如 Launcher)。

9.2 IRemoteTransition 接口

源码 : IRemoteTransition.aidl

java 复制代码
oneway interface IRemoteTransition {
    // 开始动画
    void startAnimation(in IBinder token, in TransitionInfo info,
            in SurfaceControl.Transaction t,
            in IRemoteTransitionFinishedCallback finishCallback);

    // 合并动画(新 Transition 合并到正在播放的动画中)
    void mergeAnimation(in IBinder transition, in TransitionInfo info,
            in SurfaceControl.Transaction t, in IBinder mergeTarget,
            in IRemoteTransitionFinishedCallback finishCallback);

    // 接管正在播放的动画
    void takeOverAnimation(in IBinder transition, in TransitionInfo info,
            in SurfaceControl.Transaction t,
            in IRemoteTransitionFinishedCallback finishCallback,
            in WindowAnimationState[] states);

    // 动画被其他 Handler 消费
    void onTransitionConsumed(in IBinder transition, in boolean aborted);
}

9.3 IRemoteTransitionFinishedCallback

源码 : IRemoteTransitionFinishedCallback.aidl

java 复制代码
interface IRemoteTransitionFinishedCallback {
    void onTransitionFinished(in WindowContainerTransaction wct,
            in SurfaceControl.Transaction sct);
}

远程进程动画完成后调用,可附带 WindowContainerTransaction(修改窗口状态)和额外的 Surface Transaction。

9.4 远程进程的典型实现

java 复制代码
// 远程进程(如 Launcher)实现 IRemoteTransition
@Override
public void startAnimation(IBinder token, TransitionInfo info,
        SurfaceControl.Transaction t,
        IRemoteTransitionFinishedCallback finishCallback) {
    // 1. 应用 startTransaction
    t.apply();

    // 2. 遍历 Change 列表,为每个创建动画
    for (TransitionInfo.Change change : info.getChanges()) {
        SurfaceControl leash = change.getLeash();
        if (change.getMode() == TransitionInfo.TRANSIT_OPEN) {
            ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
            anim.setDuration(300);
            anim.addUpdateListener(a -> {
                float fraction = a.getAnimatedFraction();
                SurfaceControl.Transaction ft = new SurfaceControl.Transaction();
                ft.setAlpha(leash, fraction);
                ft.setScale(leash, 0.5f + 0.5f * fraction, 0.5f + 0.5f * fraction);
                ft.apply();
            });
            anim.start();
        }
    }

    // 3. 动画完成后通知 Shell
    finishCallback.onTransitionFinished(null, null);
}

9.5 TransitionInfo.Change 关键字段

字段 类型 说明
mContainer WindowContainerToken 标识参与容器
mLeash SurfaceControl 动画 Leash --- 远程进程操作此对象
mMode int TRANSIT_OPEN/TRANSIT_CLOSE/TRANSIT_CHANGE...
mStartAbsBounds Rect 起始绝对坐标
mEndAbsBounds Rect 结束绝对坐标
mSnapshot SurfaceControl 起始状态快照
mTaskInfo RunningTaskInfo Task 信息(如果是 Task)
mFlags int 标志位(半透明、壁纸等)

10. 阶段七:finishTransition --- 完成与清理

10.1 Shell 通知 Core 完成

Shell 调用 IWindowOrganizerController.finishTransition(token, wct)

10.2 WindowOrganizerController.finishTransition

源码 : WindowOrganizerController.java

java 复制代码
public void finishTransition(@NonNull IBinder transitionToken,
        @Nullable WindowContainerTransaction t) {
    synchronized (mGlobalLock) {
        final Transition transition = Transition.fromBinder(transitionToken);
        if (t != null) {
            mTransitionController.mFinishingTransition = transition;
            applyTransaction(t, -1, chain, caller);
        }
        mTransitionController.finishTransition(chain);
        mTransitionController.mFinishingTransition = null;
    }
}

10.3 TransitionController.finishTransition

源码 : TransitionController.java

java 复制代码
void finishTransition(@NonNull ActionChain chain) {
    final Transition record = chain.mTransition;
    mPlayingTransitions.remove(record);
    updateRunningRemoteAnimation(record, false);
    record.finishTransition(chain);  // ★ 委托给 Transition 自身清理

    for (int i = mAnimatingExitWindows.size() - 1; i >= 0; i--) {
        final WindowState w = mAnimatingExitWindows.get(i);
        if (w.mAnimatingExit && w.mHasSurface && !w.inTransition()) {
            w.onExitAnimationDone();
        }
    }
    if (!inTransition()) {
        validateStates();
        mAtm.mWindowManager.onAnimationFinished();
    }
}

10.4 Transition.finishTransition --- 核心清理

源码 : Transition.java

java 复制代码
void finishTransition(@NonNull ActionChain chain) {
    // 关闭 start/finish Transaction
    if (mStartTransaction != null) mStartTransaction.close();
    if (mFinishTransaction != null) mFinishTransaction.close();

    // 应用清理 Transaction(Surface 归位、层级恢复)
    if (mCleanupTransaction != null) {
        mCleanupTransaction.apply();
        mCleanupTransaction = null;
    }

    // 提交不可见的 Activity
    for (int i = 0; i < mParticipants.size(); ++i) {
        final WindowContainer<?> participant = mParticipants.valueAt(i);
        final ActivityRecord ar = participant.asActivityRecord();
        if (ar != null && !ar.isVisibleRequested()) {
            // commitVisibility、快照、PiP 处理
        }
    }
}

10.5 清理 Transaction 的构建

buildCleanupTransaction 负责:

  • 将 Leash 下的 Surface reparent 回原始父节点
  • 销毁临时 Leash
  • 恢复层级顺序
  • 清理快照 Surface

11. 异常与取消处理

11.1 Transition 超时

BLASTSyncEngine 自带超时机制。当参与者长时间未完成首帧绘制,Transition 会自动完成并播放。

11.2 Transition 中止

源码 : Transition.java

java 复制代码
void abort() {
    if (mState == STATE_ABORT) return;  // 幂等保护
    if (mState == STATE_PENDING) {
        mState = STATE_ABORT;
        return;
    }
    if (mState != STATE_COLLECTING && mState != STATE_STARTED) {
        throw new IllegalStateException("Too late to abort. state=" + mState);
    }
    mState = STATE_ABORT;
    mSyncEngine.abort(mSyncId);  // 清理 sync
    mController.dispatchLegacyAppTransitionCancelled(mTargetDisplays);
    invokeTransitionEndedListeners();
}

11.3 Shell 进程死亡

TransitionController 通过 DeathRecipient 监听 Shell:

java 复制代码
// TransitionPlayerRecord 中
player.asBinder().linkToDeath(..., 0);

Shell 死亡后,TransitionController 清空 mTransitionPlayers,回退到 Legacy 路径或直接跳过动画。

11.4 远程动画超时

IRemoteTransition 的远程进程如果无响应,Shell 内部有超时机制强制完成。


12. 关键数据结构

12.1 Transition 状态机

复制代码
STATE_PENDING → STATE_COLLECTING → STATE_STARTED → STATE_PLAYING → STATE_FINISHED
     ↓               ↓                ↓
  STATE_ABORT    STATE_ABORT      STATE_ABORT
状态 说明
PENDING 已创建但未开始收集
COLLECTING 正在收集参与者
STARTED Shell 已确认,等待首帧绘制
PLAYING 动画播放中
FINISHED 动画完成,清理中
ABORT 已中止

12.2 TransitionInfo 结构

yaml 复制代码
TransitionInfo
├── type: int (TRANSIT_OPEN/CLOSE/CHANGE...)
├── flags: int
├── changes: List<Change>          ← 所有变更(Z-order 自顶向下)
│   └── Change
│       ├── container: WindowContainerToken
│       ├── leash: SurfaceControl         ← 动画操作对象
│       ├── mode: int                     ← OPEN/CLOSE/CHANGE
│       ├── startAbsBounds: Rect
│       ├── endAbsBounds: Rect
│       ├── snapshot: SurfaceControl      ← 起始快照
│       ├── taskInfo: RunningTaskInfo
│       └── flags: int
└── roots: List<Root>              ← 每 Display 一个根
    └── Root
        ├── displayId: int
        ├── leash: SurfaceControl         ← 根 Leash
        └── offset: Point

12.3 RemoteTransition

yaml 复制代码
RemoteTransition
├── mRemoteTransition: IRemoteTransition    ← 远程动画接口
├── mAppThread: IApplicationThread          ← 用于提升进程优先级
└── mDebugName: String                      ← 调试名称

12.4 TransitionRequestInfo

yaml 复制代码
TransitionRequestInfo
├── mType: int                              ← 过渡类型
├── mTriggerTask: RunningTaskInfo           ← 触发 Task
├── mRemoteTransition: RemoteTransition     ← 远程动画(可选)
├── mPipChange: PipChange                   ← PiP 信息
├── mDisplayChange: DisplayChange           ← Display 变化(旋转等)
├── mFlags: int                             ← 过渡标志
└── mDebugId: int                           ← 调试 ID

13. 完整时序图

13.1 Shell Transitions 正常流程

scss 复制代码
Launcher/SystemUI          ATM/WMS (Core)                    TransitionController          Shell (ITransitionPlayer)
     │                         │                                    │                            │
     │ registerTransitionPlayer│                                    │                            │
     │────────────────────────>│                                    │                            │
     │                         │ registerTransitionPlayer(player)   │                            │
     │                         │───────────────────────────────────>│                            │
     │                         │                                    │ mTransitionPlayers.add()   │
     │                         │                                    │                            │
     │ [启动 Activity]          │                                    │                            │
     │ startActivity(options)  │                                    │                            │
     │────────────────────────>│                                    │                            │
     │                         │ ActivityStarter.execute()          │                            │
     │                         │  r.takeRemoteTransition()          │                            │
     │                         │  transitionController.collect(r)   │                            │
     │                         │───────────────────────────────────>│                            │
     │                         │                                    │ Transition.collect(r)       │
     │                         │                                    │  mParticipants.add(r)       │
     │                         │                                    │  mChanges.put(r, info)      │
     │                         │                                    │                            │
     │                         │ handleStartResult()                │                            │
     │                         │  transitionController              │                            │
     │                         │   .requestStartTransition()        │                            │
     │                         │───────────────────────────────────>│                            │
     │                         │                                    │ build TransitionRequestInfo │
     │                         │                                    │                            │
     │                         │                                    │ requestStartTransition()    │
     │                         │                                    │───────────────────────────>│
     │                         │                                    │                            │
     │                         │                                    │     [Shell 决定策略]        │
     │                         │                                    │<───────────────────────────│
     │                         │                                    │ startTransition(token, wct) │
     │                         │                                    │                            │
     │                         │                                    │ Transition.start()          │
     │                         │                                    │  STATE_STARTED              │
     │                         │                                    │                            │
     │                         │         [等待参与者首帧绘制]          │                            │
     │                         │                                    │                            │
     │                         │                                    │ 构建 TransitionInfo         │
     │                         │                                    │  calculateTransitionRoots() │
     │                         │                                    │  getLeashSurface() for each │
     │                         │                                    │  buildFinishTransaction()   │
     │                         │                                    │  STATE_PLAYING              │
     │                         │                                    │                            │
     │                         │                                    │ onTransitionReady(token,    │
     │                         │                                    │   info, startT, finishT)    │
     │                         │                                    │───────────────────────────>│
     │                         │                                    │                            │
     │                         │                                    │        [应用 startT]        │
     │                         │                                    │                            │
     │                         │                                    │     [使用 RemoteTransition  │     │                         │                                    │      委托给 Launcher?]      │
     │                         │                                    │                            │
     │ IRemoteTransition       │                                    │                            │
     │  .startAnimation(...)   │                                    │                            │
     │<════════════════════════════════════════════════════════════════════════════════════════│
     │                         │                                    │                            │
     │ [操作 Leash 执行动画]     │                                    │                            │
     │                         │                                    │                            │
     │ finishCallback          │                                    │                            │
     │  .onTransitionFinished()│                                    │                            │
     │════════════════════════>│                                    │                            │
     │                         │                                    │                            │
     │                         │                                    │        [Shell 收到完成]     │
     │                         │                                    │        [应用 finishT]       │
     │                         │                                    │<───────────────────────────│
     │                         │                                    │ finishTransition(token,wct) │
     │                         │                                    │                             │
     │                         │                                    │ TransitionController        │
     │                         │                                    │  .finishTransition()        │
     │                         │                                    │  Transition.finishTransition│
     │                         │                                    │   mCleanupTransaction.apply │
     │                         │                                    │   reparent surface → parent │
     │                         │                                    │   remove leash              │
     │                         │                                    │   STATE_FINISHED            │
     │                         │                                    │                             │

13.2 异常流程

css 复制代码
场景1: Shell 进程死亡
TransitionController (DeathRecipient)
  └─ mTransitionPlayers 清空
      └─ isShellTransitionsEnabled() = false
          └─ 后续转场回退到 Legacy 或跳过动画

场景2: 参与者首帧绘制超时
BLASTSyncEngine
  └─ timeout
      └─ Transition.start() 强制进入
          └─ onTransitionReady 即使部分未就绪也发送

场景3: 远程动画无响应
Shell 内部超时
  └─ 强制调用 finishTransition()
      └─ Core 执行清理

场景4: Transition 被中止
Transition.abort()
  └─ STATE_ABORT
      └─ 清理 sync、通知 Shell onTransitionConsumed()

附录A:Leash 生命周期总结

css 复制代码
Shell Transitions 路径:

创建阶段:
  TransitionController.createAndStartCollecting(TRANSIT_OPEN)
    → new Transition(type, flags, controller, syncEngine)
    → moveToCollecting(transit)  // 进入 COLLECTING 状态
  ActivityStarter.execute()
    → Transition.collect(r)  // 收集 Activity 参与者
    → handleStartResult() → requestStartTransition()  // 请求 Shell

就绪阶段:
  Shell 调用 IWindowOrganizerController.startTransition()
    → Transition.start()  // STATE_STARTED
    → BLASTSync 等待所有参与者首帧绘制
    → 进入 PLAYING 状态

Leash 构建阶段:
  Transition 构建 TransitionInfo
    → calculateTransitionRoots() → makeAnimationLeash() 创建根 Leash
    → getLeashSurface() 获取/创建各 Target Leash
    → buildFinishTransaction() 构建归位 Transaction
    → buildCleanupTransaction() 构建清理 Transaction

传递阶段:
  ITransitionPlayer.onTransitionReady(token, info, startT, finishT)
    → Shell 收到 TransitionInfo
      → 可委托 IRemoteTransition.startAnimation()
        → 远程进程通过 change.getLeash() 操作 Surface

销毁阶段:
  IWindowOrganizerController.finishTransition(token, wct)
    → TransitionController.finishTransition()
      → Transition.finishTransition()
        → mCleanupTransaction.apply()
          → reparent(surface, parent)   // 子 Surface 回归原父
          → remove(leash)               // 销毁 Leash
          → onAnimationLeashLost()      // 通知清理
        → commitVisibility 处理不可见 Activity

附录B:Legacy App Transition 快速参考

Legacy 在 Shell 启用时被短路。以下为 Legacy 路径的关键节点:

阶段 关键方法 源码位置
注册 AppTransition.overridePendingAppTransitionRemote() AppTransition.java
决策 AppTransitionController.overrideWithRemoteAnimationIfSet() AppTransitionController.java
Leash 创建 SurfaceAnimator.createAnimationLeash() SurfaceAnimator.java
捕获 RemoteAnimationAdapterWrapper.startAnimation() RemoteAnimationController.java
启动 RemoteAnimationController.goodToGo() RemoteAnimationController.java
Target 构建 ActivityRecord.createRemoteAnimationTarget() ActivityRecord.java
Binder 调用 IRemoteAnimationRunner.onAnimationStart() AIDL 接口
完成 IRemoteAnimationFinishedCallback.onAnimationFinished() AIDL 接口
Leash 销毁 SurfaceAnimator.removeLeash() SurfaceAnimator.java
超时 TIMEOUT_MS = 10000 RemoteAnimationController.java

Legacy 核心类:

  • RemoteAnimationController --- 生命周期管理(10s 超时、死亡监听)
  • RemoteAnimationRecord/AdapterWrapper --- 连接 SurfaceAnimator 和 RemoteAnimationTarget
  • RemoteAnimationTarget --- 扁平数组(每个 Activity 一个)
  • IRemoteAnimationRunner --- onAnimationStart(targets[]) / onAnimationCancelled()

附录C:核心 QA

Q1: Shell Transitions 启用后,Legacy 还能触发吗?

不能。 Shell 启用后,Legacy 路径被完全短路:

  1. AppTransition.prepareAppTransition() 返回 false(AppTransition.java),不注册任何 Legacy 过渡
  2. 因此 AppTransition.isReady() 永远不会变为 true,handleAppTransitionReady() 不会被调用
  3. 即使 Activity Embedding 中 overrideWithTaskFragmentRemoteAnimation() 调用了 overridePendingAppTransitionRemote(),由于 prepareAppTransition 未被调用,isTransitionSet() 为 false,setReady() 也不会触发

在 Shell 模式下,所有窗口转场(包括 Activity Embedding)都通过 TransitionController 收集和处理。

Q2: Shell 路径和 Legacy 路径共享哪些机制?

  • Leash 机制 :两者都通过 SurfaceControl Leash 实现,但创建方式不同。Shell 通过 Transition.calculateTransitionRoots() 直接创建;Legacy 通过 SurfaceAnimator.createAnimationLeash()
  • SurfaceControl.Transaction:两者都使用 Transaction 操作 Surface
  • ActivityRecord/Task/WindowContainer:同一套窗口层级结构

Q3: RemoteTransition 和 RemoteAnimationAdapter 有什么区别?

维度 RemoteTransition (Shell) RemoteAnimationAdapter (Legacy)
接口 IRemoteTransition.startAnimation() IRemoteAnimationRunner.onAnimationStart()
数据 TransitionInfo(层级结构) RemoteAnimationTarget[](扁平数组)
完成回调 IRemoteTransitionFinishedCallback IRemoteAnimationFinishedCallback
附加能力 mergeAnimation(), takeOverAnimation() 仅 start/cancel
使用者 Shell 委托 Core 直接调用

Q4: Shell Transitions 相比 Legacy 的设计差异?

  1. 层级感知TransitionInfo 保留完整的窗口层级关系,动画可以基于 Task/TaskFragment 整体操作;Legacy 的 RemoteAnimationTarget[] 是扁平数组
  2. 并行支持:多 Track 允许多个 Transition 同时播放;Legacy 同一时间只能有一个
  3. 编排灵活性:Shell 可以 veto、修改或委托 Transition,而 Legacy 由 Core 单方面决定
  4. 合并能力IRemoteTransition.mergeAnimation() 允许新动画合并到正在播放的动画中;Legacy 不支持
  5. 同步机制 :Shell 使用 BLASTSync 精确等待首帧;Legacy 使用 AppTransition.isReady() 轮询

Q5: Leash 机制是什么?为什么要引入 Leash?

Leash 是 WMS 为每个参与动画的 WindowContainer 创建的一个中间 SurfaceControl 层。动画期间,原始窗口 Surface 被 reparent 到 Leash 下,远程进程只操作 Leash(alpha、scale、translation 等),不直接接触原始 Surface。

引入原因:

  1. 安全隔离:原始 Surface 控制权始终在 WMS 手中,远程进程无法恶意操作或拒绝释放
  2. 互不干扰:动画过程中 Activity 仍通过 Choreographer 正常绘制新帧,两者互不干扰
  3. 快速复位 :动画结束后一行 reparent(surface, parent) 即可毫秒级恢复原始层级,remove(leash) 销毁 Leash

Q6: BLASTSync 是什么?在动画中起什么作用?

BLASTSync 是 Android 的帧同步机制。在 Transition 收集阶段,每个参与者被加入 BLASTSyncEngine 的 SyncSet,系统等待所有参与者完成首帧绘制后才进入 PLAYING 状态。

这保证了 onTransitionReady 发送给 Shell 时,所有参与者的 Surface 都已有内容可显示,避免动画中出现黑屏/白屏闪烁。

Q7: Transition 的目标提升(promotion)是什么?

当同一个父容器下的多个子容器都参与同一个 Transition 时,系统会将动画目标提升到更高层级(例如从 Activity 提升到 Task)。这样做的好处:

  • 只需一个 Leash 统一控制,避免多个独立 Leash 导致的画面撕裂
  • 远程进程处理更简单,只需操作一个 Leash
  • Transition.calculateTransitionRoots() 中通过 findCommonAncestor() 找到公共祖先来决定提升层级

Q8: startTransaction 和 finishTransaction 分别做什么?

  • startTransaction:动画开始前应用。设置 Leash 的初始位置、可见性、crop 等,确保动画从正确的起始状态开始
  • finishTransaction :动画结束后应用。将 Surface 从 Leash reparent 回原始父节点,恢复正确的 Z-order 层级,重置 alpha/scale 等属性

两者都由 Transition.javaonTransitionReady 之前构建,随 TransitionInfo 一起发送给 Shell。Shell 需要在正确的时机 apply 它们。

Q9: RemoteTransition 的 mergeAnimation 有什么用?

当一个新的 Transition 需要播放,但当前已有一个相同类型的动画正在播放时,Shell 可以选择将新 Transition 合并到现有动画中,而不是打断重新开始。

典型场景:用户快速连续启动两个 Activity,第二个启动动画可以合并到第一个中,提供更流畅的视觉体验。IRemoteTransition.mergeAnimation() 就是 Shell 用来委托这个合并操作的接口。

Q10: Shell 进程死亡后 WMS 怎么处理?

TransitionController 通过 DeathRecipient 监听 Shell 进程:

  1. Shell 死亡 → binderDied() 回调触发
  2. TransitionController 清空 mTransitionPlayers
  3. isShellTransitionsEnabled() 返回 false
  4. 正在播放的 Transition 通过 flushRunningTransitions() 强制完成(apply cleanup transaction)
  5. 后续窗口转场没有动画,直接切换
  6. Shell 重启后重新调用 registerTransitionPlayer() 恢复正常

Q11: 如何自定义 App 启动动画?

Launcher 可以通过以下方式自定义启动动画:

java 复制代码
// 通过 ActivityOptions 传入 RemoteTransition(Shell 模式)
RemoteTransition transition = new RemoteTransition(
    myRemoteTransitionImpl,  // 实现 IRemoteTransition 接口
    appThread,
    "MyCustomLaunchAnimation"
);
ActivityOptions options = ActivityOptions.makeRemoteAnimation(transition);
startActivity(intent, options.toBundle());

动画实现中,通过 TransitionInfo.getChanges() 获取所有变化的 Leash,然后用 ValueAnimatorSpringAnimation 驱动 alpha/scale/translation 等属性,完成后调用 finishCallback.onTransitionFinished()

Q12: 为什么动画期间要禁用输入事件?

防止 点击劫持(Tapjacking)。动画期间,窗口处于不受当前应用直接控制的 Leash 中,恶意远程动画可能在转场期间伪造 UI 界面,诱骗用户点击。

  • Shell Transitions 路径 :WMS 通过 ActivityRecordInputSinkfinishTransition() 时更新 input-sink 状态
  • Legacy 路径 :通过 setDropInputForAnimation(true) 丢弃所有输入事件,动画结束后恢复

相关推荐
朱涛的自习室2 小时前
30天11万行代码,我用 Trae 和 Gemini 造了个 AI 测试引擎
android·前端·人工智能
Digitally2 小时前
如何删除三星 Galaxy 手机中的重复音乐?
android
ch_ziyuan2 小时前
2026新优化神马TV8.5影视点播系统保姆级搭建教程:三后台配置+反编译修改
android·ios·php
帅次2 小时前
Jetpack Compose 动画实战:animateFloatAsState、AnimatedVisibility 与 graphicsLayer 避坑
android·kotlin·gradle·android jetpack
问心无愧05133 小时前
ctf show web 入门258
android·前端·笔记
AI玫瑰助手3 小时前
Python函数:def定义函数与参数传递基础
android·开发语言·python
Maddie_Mo3 小时前
Pi Agent Web 使用教程:把本地 Pi Coding Agent 搬进浏览器
android·java·前端·人工智能·ai
码不停蹄的玄黓3 小时前
MySQL索引设计核心注意事项
android·数据库·mysql
ch_ziyuan4 小时前
安卓APP报毒自动化解决方案处理系统:动态包名+证书随机+360加固集成(后台源码)
android·运维·自动化