Shell Transition 机制详解
面向 WMS 开发者的完整指南
基于 android 16 代码库分析,覆盖 Shell Transition 架构。 关键源码路径:
frameworks/base/services/core/java/com/android/server/wm/、frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/transition/
一、为什么要理解 Shell Transition
1.1 传统 App Transition 的局限
在 Shell Transition 出现之前,WMS 自己管理动画(AppTransition),存在三个问题:
- WMS 耦合过重:动画逻辑与窗口管理逻辑混在一起,改动画需要改 WMS 核心代码
- 扩展性差:难以支持折叠屏展开/折叠、分屏到自由窗口切换等复杂场景
- 多 Surface 同步困难:多个窗口(旧 Activity + 新 Activity + Task 容器)动画时,各自 buffer 到达 SurfaceFlinger 的时间不可控,容易出现闪烁、撕裂
1.2 Shell Transition 的核心思想
WMS 只负责窗口状态管理,Shell(SystemUI)负责动画播放。 两者通过 Binder IPC 通信。
关键 AIDL 接口 ITransitionPlayer(frameworks/base/core/java/android/window/ITransitionPlayer.aidl):
java
oneway interface ITransitionPlayer {
// WMS → Shell:通知 Shell 启动过渡
void requestStartTransition(in IBinder transitionToken, in TransitionRequestInfo request);
// WMS → Shell:通知 Shell 动画可以开始(所有参与者已绘好)
void onTransitionReady(in IBinder transitionToken, in TransitionInfo info,
in SurfaceControl.Transaction t, in SurfaceControl.Transaction finishT);
}
Shell 通过 WindowOrganizerController(frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java)回告 WMS。
二、架构概览
2.1 进程边界与核心类
scss
┌──────────────────────────┐ ┌──────────────────────────────┐
│ WMS (system_server) │ │ Shell (SystemUI 进程) │
├──────────────────────────┤ ├──────────────────────────────┤
│ TransitionController │──request───→│ Transitions │
│ .requestStartTransition│ StartTrans │ .requestStartTransition() │
│ .moveToCollecting │ │ ├─ dispatchRequest() │
│ .moveToPlaying │ │ └─ mOrganizer.startTransition()│
│ │ │ ↓ (Binder 回告) │
│ Transition (状态机) │ │ │
│ .onTransactionReady │──onTrans──→│ TransitionHandler 链: │
│ .finishTransition │ itionReady │ ┌──────────────────────┐ │
│ │ │ │DefaultTransitionHandler│ │
│ BLASTSyncEngine │ │ │RemoteTransitionHandler │ │
│ .onSurfacePlacement │ │ │FoldTransitionHandler │ │
│ │ │ └──────────────────────┘ │
│ WindowOrganizerController│←──finish───│ │
│ .startTransition │ Transition│ Observer 链: │
│ .finishTransition │ (Binder) │ HomeTransitionObserver │
│ │ │ FocusTransitionObserver │
└──────────────────────────┘ │ .finishTransitionWithState() │
└──────────────────────────────┘
2.2 关键类速查
| 类 | 进程 | 核心职责 | 源码路径 |
|---|---|---|---|
TransitionController |
WMS | 过渡的创建、收集、播放、完成全流程编排 | .../wm/TransitionController.java |
Transition |
WMS | 单个 transition 的状态机 + BLAST 同步回调入口 | .../wm/Transition.java |
BLASTSyncEngine |
WMS | 等待所有参与窗口绘制完成,合并事务 | .../wm/BLASTSyncEngine.java |
Transitions |
Shell | Shell 端过渡总控,分发动画到 Handler 链 | .../transition/Transitions.java |
TransitionPlayerImpl |
Shell | ITransitionPlayer.Stub 实现,接收 WMS 通知 |
.../transition/Transitions.java (inner) |
DefaultTransitionHandler |
Shell | 系统默认动画处理器 | .../transition/DefaultTransitionHandler.java |
WindowOrganizerController |
WMS | Shell→WMS 的 Binder 服务端,处理 startTransition() / finishTransition() |
.../wm/WindowOrganizerController.java |
三、完整流程:从点击到动画结束
3.1 时序图
3.2 阶段详解
阶段 1:创建 Transition 并请求 Shell 启动
入口 :Activity 启动导致窗口层级变化(ActivityRecord.setVisible() → collectExistenceChange() 等)
scss
startActivity()
→ ActivityRecord.setVisible() / removeApp()
→ mTransitionController.collect(this) // 收集参与者
→ mTransitionController.requestTransitionIfNeeded()
→ createTransition(type, flags) // 创建 Transition
→ moveToCollecting(transition) // 进入收集状态
→ requestStartTransition(transition, ...) // 通知 Shell
→ mPlayer.requestStartTransition(token, request) // Binder 调用
阶段 2:Handler 认领 + 同步回告 startTransition
这是整个流程中最容易误解的环节 。startTransition 不是 Shell "处理完" 之后再发起的独立回告,而是 requestStartTransition() 方法内部同步执行的最后一步。
Shell 侧完整流程 (Transitions.requestStartTransition(), 第 1860 行):
java
void requestStartTransition(@NonNull IBinder transitionToken,
@Nullable TransitionRequestInfo request) {
// 步骤 1: 创建 ActiveTransition 放入 mKnownTransitions
final ActiveTransition active = new ActiveTransition(transitionToken);
mKnownTransitions.put(transitionToken, active);
// 步骤 2: Handler 链认领
Pair<TransitionHandler, WindowContainerTransaction> requestResult =
dispatchRequestWithTracing(transitionToken, request, /* skip= */ null);
active.mHandler = requestResult.first;
wct = requestResult.second;
// 步骤 3: 显示变化分发(如旋转)
if (request.getDisplayChange() != null) {
mDisplayController.onDisplayChangeRequested(wct, ...);
}
// 步骤 4: 同步回告 WMS 正式启动(同一方法内!)
mOrganizer.startTransition(transitionToken, wct); // ← 直接调用,不延迟
}
WMS 侧 (WindowOrganizerController.startTransition(), 第 367 行):
java
public void startTransition(@NonNull IBinder transitionToken,
@Nullable WindowContainerTransaction t) {
// 通过 token 找到 WMS 侧的 Transition 对象
Transition transition = Transition.fromBinder(transitionToken);
// 检查 transition 是否还在收集状态
if (!transition.isCollecting() && !transition.isForcePlaying()) {
// Shell 响应超时,WM 状态可能已不正确
Slog.e(TAG, "Trying to start a transition that isn't collecting...");
}
// 关键:调用 transition.start() → STATE_COLLECTING → STATE_STARTED
final ActionChain chain = mService.mChainTracker.start("startTransit", transition);
transition.start(); // ← 状态切换
// 应用 Shell 返回的 WCT(如有)
applyTransaction(wct, -1 /* syncId */, chain, caller);
}
transition.start() 做了什么 (Transition.java):
java
void start() {
if (mState != STATE_COLLECTING) return;
mState = STATE_STARTED; // ← 核心:进入 STARTED 状态
// 此时 BLAST 同步引擎开始等待所有参与窗口绘制完成
// 但还没有调用 setAllReady()!
}
注意 :startTransition 只把状态从 STATE_COLLECTING 推进到 STATE_STARTED,start() 本身只调用 applyReady(),不调用 setAllReady()。后续推进到播放并非依赖 setAllReady()------setAllReady() 属于另一条独立路径(如 playNow()、直接由 Shell 创建的 transition 中的 WindowOrganizerController.setAllReadyIfNeeded()),不在 BLAST 同步完成 → onTransactionReady 这条主流程内。
阶段 3:BLAST 同步 + 回调 Shell
这是整个机制的核心,详见第四节。
阶段 4:Shell 播放动画
scss
Transitions.onTransitionReady(token, info, startT, finishT)
→ dispatchReady(active)
→ processReadyQueue()
→ playTransition(active)
→ active.mHandler.startAnimation(token, info, startT, finishT, onFinish)
→ DefaultSurfaceAnimator.buildSurfaceAnimation()
→ ValueAnimator 逐帧 → SurfaceControl.Transaction.apply()
阶段 5:动画结束,WMS 清理
css
Shell: onAnimationFinished → onFinish(token, wct)
→ mOrganizer.finishTransitionWithState(token, wct, null, state)
WMS: WindowOrganizerController.finishTransitionWithState()
→ TransitionController.finishTransition(chain)
→ Transition.finishTransition(chain)
→ 从 mPlayingTransitions 移除
→ 提交不可见容器、清理 starting window、通知监听器
四、BLAST 同步机制深度解析
4.1 BLAST 解决的问题
多个窗口(旧 Activity、新 Activity、Task 容器、Wallpaper 等)各自绘制完成后,buffer 到达 SurfaceFlinger 的时间不可控。如果各自提交,会出现闪烁、撕裂。
BLAST(Buffer-Limited Asynchronous Surface Transactions)的做法是:等待所有窗口都准备好,合并所有事务,统一提交。
4.2 三个核心概念
4.2.1 SyncState(每个 WindowContainer 的同步状态)
java
SYNC_STATE_NONE = 0 // 不参与 sync,Surface 操作写入 mPendingTransaction
SYNC_STATE_WAITING_FOR_DRAW = 1 // 已加入 sync,正在等 App 绘制完成
SYNC_STATE_READY = 2 // 已就绪(自身 OK,但子窗口可能还在绘制)
当 prepareSync() 被调用时,整个子树的 mSyncState 被设为 SYNC_STATE_READY。
4.2.2 SyncGroup(SyncGroup)
一个 SyncGroup 代表一组需要同步的窗口。它维护:
mSyncId:唯一标识mRootMembers:参与的WindowContainer列表mDependencies:依赖的其他 SyncGroup(处理重叠场景)mOrphanTransaction:合并所有成员的事务mListener:回调onTransactionReady()的监听器(即Transition自己)
4.2.3 Sync Transaction(每个容器的隔离事务)
WindowContainer 有两个事务字段:
mSyncTransaction |
mPendingTransaction |
|
|---|---|---|
| 用途 | sync 模式下的隔离事务 | 非 sync 模式下的默认事务 |
| 何时写入 | mSyncState != SYNC_STATE_NONE 时,getSyncTransaction() 返回它 |
否则,getPendingTransaction() 返回它 |
| 何时合并 | finishSync() 递归合并到 SyncGroup.mOrphanTransaction |
最终合并到 DisplayContent 的 pending |
关键代码 (WindowContainer.getSyncTransaction()):
java
public Transaction getSyncTransaction() {
if (mSyncTransactionCommitCallbackDepth > 0) {
return mSyncTransaction;
}
if (mSyncState != SYNC_STATE_NONE) {
// sync 模式下,所有 Surface 操作写入隔离事务
return mSyncTransaction;
}
return getPendingTransaction(); // 非 sync 模式,走默认路径
}
4.3 同步流程
scss
┌────────────────────────────────────────────────────────────────┐
│ 1. WMS 调用 prepareSync() │
│ 递归设置所有参与 WindowContainer 的 mSyncState = READY │
│ → 此后 getSyncTransaction() 返回 mSyncTransaction │
└────────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────────┐
│ 2. 所有 Surface 操作(show/hide/reparent/setPosition/setLayer)│
│ 通过 getSyncTransaction() → 写入各容器的 mSyncTransaction │
│ 隔离!不会和 transition 之外的操作混在一起 │
└────────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────────┐
│ 3. App 绘制完成 → WindowState.finishDrawing(postDrawT) │
│ → onSyncFinishedDrawing() → mSyncState = READY │
│ → DrawHandler 将事务合并到 mSyncTransaction │
└────────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────────────────────────────────────┐
│ 4. WMS surface placement → onSurfacePlacement() │
│ 遍历所有活跃 SyncGroup,调用 tryFinish(): │
│ - mReady == true? │
│ - mDependencies 为空? │
│ - 所有 mRootMembers 都 isSyncFinished()? │
│ → 全部满足 → finishNow() │
└────────────────────────────────────────────────────────────────┘
↓
────────────────────────────────────────────────────────────────┐
│ 5. finishNow() │
│ - 递归 finishSync() 合并所有 mSyncTransaction │
│ - merged = mWm.mTransactionFactory.get() │
│ - merged.merge(mOrphanTransaction) │
│ - mListener.onTransactionReady(syncId, merged) │
│ - → Transition.onTransactionReady(syncId, mergedTransaction)│
└────────────────────────────────────────────────────────────────┘
4.4 Transition 的回调
Transition 实现了 BLASTSyncEngine.TransactionReadyListener 接口。onTransactionReady() 是核心回调(Transition.java:2427):
java
public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) {
// 1. 提交可见 Activity 和 Wallpaper 的窗口可见性
commitVisibleActivities(transaction);
commitVisibleWallpapers(transaction);
// 2. 计算动画目标(从参与者提升为可动画的祖先)
mTargets = calculateTargets(mParticipants, mChanges, mType, mFlags);
// 3. 计算 TransitionInfo(给 Shell 的数据包)
TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
// 4. 分配轨道(决定哪些 transition 可以并行播放)
mController.assignTrack(this, info);
// 5. 进入播放状态
mController.moveToPlaying(this);
// 6. 通过 Binder 回调 Shell
mController.getTransitionPlayer().onTransitionReady(
mToken, info, transaction /* startT */, mFinishTransaction);
}
4.5 Sync Method 的选择
java
// TransitionController.java:116-118
static final int SYNC_METHOD =
SystemProperties.getBoolean("persist.wm.debug.shell_transit_blast", false)
? BLASTSyncEngine.METHOD_BLAST : BLASTSyncEngine.METHOD_NONE;
METHOD_NONE:App 自己内部绘制报告,不做 BLAST 级别的 buffer 同步METHOD_BLAST:完整的 BLAST 同步,等待所有窗口的 buffer 到达
五、WMS → Shell 的两次通知详解
5.1 第一次:requestStartTransition
调用方 : TransitionController.requestStartTransition() (TransitionController.java:901) Binder 接口 : ITransitionPlayer.requestStartTransition(token, request)
参数 1: token
Transition.mToken,在createTransition()时由new Binder()创建- 整个 transition 的唯一标识
参数 2: request (TransitionRequestInfo)
java
new TransitionRequestInfo(
transition.mType, // 过渡类型: TRANSIT_OPEN / CLOSE / TO_FRONT / TO_BACK / CHANGE 等
triggerTask, // 触发此 transition 的 Task(RunningTaskInfo,对应 mTriggerTask/getTriggerTask())
pipChange, // 画中画变化信息(如有)
remoteTransition, // App 注册的远程动画(IRemoteTransition)
displayChange, // 显示变化(如旋转时的 bounds/rotation 变化)
transition.getFlags() // 过渡标志位
)
注:
TransitionRequestInfo没有getSyncId()字段;BLAST sync ID 由Transition自身持有,不通过 request 传给 Shell。
关键点 :此时 Shell 还没有收到任何窗口信息!request 只告诉 Shell "要发生什么类型的过渡",Shell 据此决定用哪个 TransitionHandler 来认领。
5.2 第二次:onTransitionReady
调用方 : Transition.onTransactionReady() 内部 (Transition.java:2767) Binder 接口 : ITransitionPlayer.onTransitionReady(token, info, startT, finishT)
参数 2: info (TransitionInfo)
type+flags- Roots: 过渡涉及的顶层容器(Task/DisplayArea)
- Changes: 每个参与容器的详细变化信息(mode、startAbsBounds、parent、leash Surface、flags 等)
参数 3: startT (SurfaceControl.Transaction)
- 即 BLAST 回调
onTransactionReady(syncId, transaction)的参数 - 包含所有参与窗口第一帧绘制完成后的 Surface 状态(show/hide、位置、大小、parent 关系等)
- Shell 通过
setupAnimHierarchy()在此基础上建立动画层级
参数 4: finishT (SurfaceControl.Transaction)
- 由
mTransactionFactory.get()新建 - 记录动画结束后窗口需要恢复的最终状态
六、startT 和 finishT 详解
6.1 startT 的来源
直接来源 : Transition.onTransactionReady(int syncId, SurfaceControl.Transaction transaction) 的参数
深层来源 : BLASTSyncEngine.SyncGroup.finishNow():
java
SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
if (mOrphanTransaction != null) {
merged.merge(mOrphanTransaction); // 合并所有参与者的 mSyncTransaction
}
// → 回调 → Transition.onTransactionReady(syncId, merged)
mOrphanTransaction 是 finishSync() 递归合并的结果:
java
void finishSync(Transaction outMergedTransaction, ...) {
outMergedTransaction.merge(mSyncTransaction); // 合并自身
for (child : children) {
child.finishSync(outMergedTransaction, group, cancel); // 递归子节点
}
}
6.2 什么时候 apply
| Transaction | Apply 时机 | 谁执行 |
|---|---|---|
| startT | 不直接 apply。Shell 的 DefaultSurfaceAnimator 用它作为基础,每帧通过 SurfaceControl.Transaction 提交新的变换给 SurfaceFlinger |
Shell,逐帧 |
| finishT | 动画全部播放结束后,Shell 调用 onFinish() → WMS finishTransition() 中 apply |
WMS(最终阶段) |
七、Transition 状态机
7.1 状态定义
scss
STATE_PENDING → 刚创建,等待开始
STATE_COLLECTING → 正在收集参与者(collect() 调用中)
STATE_STARTED → Shell 回告 startTransition() 后,BLAST 开始等待所有窗口绘制
STATE_PLAYING → onTransactionReady() 后,Shell 已收到通知开始动画
STATE_FINISHED → Shell 动画完成,WMS 已清理
STATE_ABORT → transition 被中止(超时、SystemUI 崩溃等)
关键方法:
moveToCollecting()→ 进入STATE_COLLECTINGstart()→ 进入STATE_STARTED(由WindowOrganizerController.startTransition()调用)setAllReady()→ 标记所有就绪条件已满足,触发applyReady()(独立路径,如playNow()/setAllReadyIfNeeded(),非 BLAST→onTransactionReady主流程必经)onTransactionReady()→ 进入STATE_PLAYING,通知 ShellfinishTransition()→ 进入STATE_FINISHED,清理 WM 状态abort()→ 进入STATE_ABORT
状态流转完整路径:
css
STATE_COLLECTING
└── WMS: moveToCollecting() → 开始 collect() 参与者
└── WMS: requestStartTransition() → Binder → Shell
└── Shell: dispatchRequest() → Handler 认领
└── Shell: mOrganizer.startTransition() → Binder → WMS (← 同一方法内同步调用!)
└── WMS: WindowOrganizerController.startTransition()
└── WMS: transition.start() → STATE_STARTED
└── WMS: applyTransaction(wct)
└── BLAST: 等待所有参与窗口绘制完成
└── BLAST: onSurfacePlacement() → tryFinish() → 全部就绪
└── BLAST: finishNow() → onTransactionReady() 回调
└── Transition: onTransactionReady() → calculateTargets() → moveToPlaying() → STATE_PLAYING
└── WMS: ITransitionPlayer.onTransitionReady() → Binder → Shell
└── Shell: playTransition() → 播放动画
└── Shell: onFinish() → finishTransitionWithState() → Binder → WMS
└── WMS: finishTransition() → STATE_FINISHED
7.2 STATE_STARTED 的特殊意义
STATE_STARTED 是理解整个 pipeline 的关键。isCollecting() 方法对以下两个状态都返回 true:
java
boolean isCollecting() {
return mState == STATE_COLLECTING || mState == STATE_STARTED;
}
这意味着 进入 STATE_STARTED 后,transition 仍然被认为是 "collecting" 状态 ,mCollectingTransition 仍然指向它。这是为了防止另一个 transition 抢占 mCollectingTransition 位置(除非满足"伪并行"条件,见第八节)。
STATE_STARTED 的另一个关键查询方法是 isPopulated():
java
boolean isPopulated() {
return mState >= STATE_STARTED && allReady();
}
只有当 isPopulated() 为 true 时(即 transition 已经进入 STATE_STARTED 且所有参与者已就绪),才允许新的 transition 与它"并行"------此时旧的 transition 才会被移入 mWaitingTransitions。
7.3 状态查询速查表
| 方法 | 返回 true 的条件 | 用途 |
|---|---|---|
isCollecting() |
STATE_COLLECTING 或 STATE_STARTED |
判断是否还在收集阶段 |
isPopulated() |
state >= STATE_STARTED && allReady() |
判断是否可以被新的 transition 替代 |
isPlaying() |
STATE_PLAYING |
判断动画是否正在播放 |
allReady() |
所有参与者绘制完成、BLAST 同步完成 | 内部判断是否满足 onTransactionReady 条件 |
八、TransitionController 的三个队列与"伪并行"收集
8.1 三个核心数据结构
TransitionController 维护三个 transition 容器,管理 transition 从创建到播放的全生命周期:
java
// 正在收集参与者的 transition。所有 WM 变化都会归入这个 transition。
private Transition mCollectingTransition = null;
// 已 populated(收集完成)但仍在等 BLAST 同步绘制完成的 transition
final ArrayList<Transition> mWaitingTransitions = new ArrayList<>();
// 排队等待开始收集的 transition(连并行收集条件都不满足)
final ArrayList<QueuedTransition> mQueuedTransitions = new ArrayList<>();
| 队列 | 含义 | transition 状态 | 触发条件 |
|---|---|---|---|
mCollectingTransition |
当前正在收集参与者 | STATE_COLLECTING 或 STATE_STARTED |
无活跃 transition 时直接成为 |
mWaitingTransitions |
已 populated,等 BLAST 同步 | STATE_STARTED,isPopulated()=true |
并行收集时旧的被移入 |
mQueuedTransitions |
排队等待开始收集 | 还未创建 Transition 对象 | 不能并行收集时被排入 |
关键区别 :mWaitingTransitions 中的 transition 已经完成收集 (不再收集参与者),只是在等 BLAST 同步完成。它和 mQueuedTransitions 的本质区别是后者还没有开始收集。
8.2 新 transition 申请的决策流程
当业务申请新 transition 时,TransitionController 按以下优先级处理:
- 如果 queue 不为空 → 直接排队
- 如果没有 active sync 或没有 collecting transition → 直接
moveToCollecting() - 如果正在 collecting 且满足并行条件 (
canStartCollectingNow())→ 旧的移入mWaitingTransitions,新的成为mCollectingTransition - 如果不满足并行条件 → 排入
mQueuedTransitions等待
8.3 什么是"伪并行"收集
"并行收集"并不意味着两个 transition 同时在做 collect()。任何时候 mCollectingTransition 只有一个。
所谓的"并行",实际是 不同 transition 处于 pipeline 的不同阶段:
less
旧的 transition: 已 populated,等 BLAST 同步(不再收集参与者)
新的 transition: 正在收集参与者
旧的已经收集完了,只是在等 BLAST 绘制完成,所以让出 mCollectingTransition 位置给新的。真正的并行点是让两个 transition 的 BLAST sync 可以并发等待绘制。
8.4 并行收集的前置条件
canStartCollectingNow() 检查三个条件:
- 当前 collecting 已 populated (
isPopulated()=true,即 state >= STARTED 且所有参与者就绪) - 新 transition 与当前 collecting 独立 (
getCanBeIndependent()返回 true) - 新 transition 与所有 waiting transitions 都独立
任一条件不满足,新 transition 就会被排入 mQueuedTransitions。
8.5 getCanBeIndependent() 的独立性判断
| 场景 | 是否独立 | 原因 |
|---|---|---|
| 同 display 上的窗口变化 | 否 | 参与者可能重叠,动画需要同屏协调 |
| 不同 display | 可能 | 物理隔离,动画互不影响 |
| recents + 普通 app launch | 可能 | recents 是 PARALLEL_TYPE_RECENTS 特殊类型 |
TRANSIT_PARTIAL_ENTER_SPLIT_SCREEN |
是 | 部分分屏被允许并行 |
| 默认情况 | 否 | 默认返回 false |
关键结论 :同 display 上的窗口变化(如分屏→自由窗口+全屏)getCanBeIndependent() 返回 false,只能排队或合并到一个 transition 中 。这就是为什么 TRANSIT_SPLIT_TO_FREEFORM_AND_FULL 是一个 transition 统一处理,Shell 统一播放动画,而不是两个 transition 分别播放。
8.6 队列的接力机制
三个队列之间存在"接力"关系:
scss
mQueuedTransitions → mCollectingTransition → mWaitingTransitions → mPlayingTransitions
↑ ↑
└─── tryStartCollectFromQueue() ─────┘
关键流转点:
- moveToPlaying() :collecting transition 进入 playing 后,从
mWaitingTransitions取出第一个升为 collecting - tryStartCollectFromQueue() :当 collecting 被移走后,从
mQueuedTransitions取出第一个尝试启动,如果仍不能并行,旧的继续移 waiting - onAbort():abort 的 transition 被移除后,waiting 中的第一个替补成为 collecting
九、过渡类型
9.1 AOSP 基础过渡
| 类型 | 说明 |
|---|---|
TRANSIT_OPEN |
打开(窗口/容器打开) |
TRANSIT_CLOSE |
关闭(窗口/容器关闭) |
TRANSIT_TO_FRONT |
移到前台 |
TRANSIT_TO_BACK |
移到后台 |
TRANSIT_CHANGE |
窗口属性变化(resize 等) |
TRANSIT_SLEEP |
息屏 |
注:现代 AOSP 已不再使用
TRANSIT_ACTIVITY_OPEN/CLOSE、TRANSIT_TASK_OPEN/CLOSE、TRANSIT_ROTATE这些旧名,统一归并为TRANSIT_OPEN/CLOSE/TO_FRONT/TO_BACK/CHANGE。
9.2 多窗口/分屏过渡
| 类型 | 说明 |
|---|---|
TRANSIT_SPLIT_SCREEN_PAIR_OPEN |
分屏配对打开 |
TRANSIT_SPLIT_TO_FREEFORM_AND_FULL |
分屏到自由窗口 + 全屏 |
TRANSIT_FREEFORM_TO_MAGIC |
自由窗口到 Magic 窗口 |
十、开发调试指南
10.1 关键 log 标签
| Tag | 模块 | 用途 |
|---|---|---|
TransitionController |
WMS | 过渡的创建、收集、播放、完成 |
Transition |
WMS | Transition 状态机、onTransactionReady |
BLASTSyncEngine |
WMS | 同步组的状态检查、合并 |
Transitions |
Shell | Shell 端过渡生命周期 |
WM_DEBUG_WINDOW_TRANSITIONS |
ProtoLog | WMS 过渡详细调试 |
WM_SHELL_TRANSITIONS |
ProtoLog | Shell 过渡详细调试 |
10.2 常见问题定位
| 问题 | 检查点 |
|---|---|
| 过渡未触发 | TransitionController.requestTransitionIfNeeded() 是否被调用;mTransitionPlayers 是否为空(SystemUI 是否运行) |
| 过渡卡在 collecting | 检查 Shell 是否收到 requestStartTransition 并回告了 startTransition;检查 WindowOrganizerController.startTransition() 中 transition 是否仍处于 isCollecting() 状态(Shell 超时会导致 WM 状态不正确) |
| 过渡卡在 started 不进入 playing | 检查 BLAST 同步是否正常工作;检查 onSurfacePlacement() 是否被触发;检查 tryFinish() 中 isSyncFinished() 返回值 |
| Shell 收到但无动画 | 检查 dispatchReady() 中的 track 分配;检查 Handler 链是否有人认领 |
| 动画闪烁/撕裂 | 检查 BLAST sync 是否生效(persist.wm.debug.shell_transit_blast);检查 mSyncTransaction 是否正确隔离 |
| 过渡超时 | 检查 onChangeTimeout() / onReadyTimeout();确认 App 是否正常完成 performTraversals() |
| 新 transition 未立即启动 | 检查 mQueuedTransitions 是否非空(被排队);检查 canStartCollectingNow() 返回 false 的原因:isPopulated() 为 false 或 getCanBeIndependent() 返回 false |
| 过渡卡在 waiting 不进入 playing | 检查 mWaitingTransitions 中的 transition 是否 isPopulated()=true;检查 moveToPlaying() 是否被调用;确认 BLAST sync 是否已超时或异常 |
| 同 display 出现两个 transition | 检查是否意外设置了 PARALLEL_TYPE_RECENTS;检查 getCanBeIndependent() 是否正确返回 false;同 display 上的窗口变化应合并到一个 transition 中 |
10.3 常用调试命令
bash
# 开启 BLAST 同步
adb shell setprop persist.wm.debug.shell_transit_blast true
# 开启 Shell 过渡旋转
adb shell setprop persist.wm.debug.shell_transit_rotate true
# dump 当前过渡状态
adb shell dumpsys activity service com.android.server.wm.TransitionController
# 查看 WMS 过渡日志
adb logcat -s TransitionController Transition BLASTSyncEngine
# 查看 Shell 过渡日志
adb logcat -s Transitions DefaultTransitionHandler
十一、源码速查索引
| 功能 | 文件 | 关键方法/行号 |
|---|---|---|
| 创建 Transition | TransitionController.java |
createTransition() / moveToCollecting() / requestStartTransition() |
| Transition 状态机 | Transition.java |
mState 字段 / setAllReady() / onTransactionReady() / finishTransition() |
| BLAST 同步 | BLASTSyncEngine.java |
startSyncSet() / addToSyncSet() / onSurfacePlacement() / tryFinish() / finishNow() |
| 隔离事务 | WindowContainer.java |
getSyncTransaction() / prepareSync() / finishSync() / onSyncFinishedDrawing() |
| Shell 接收通知 + 回告 | Transitions.java |
requestStartTransition() / startTransition() / onTransitionReady() / playTransition() / onFinish() |
| 动画播放 | DefaultSurfaceAnimator.java |
buildSurfaceAnimation() / AnimationAdapter |
| Binder 回告 | WindowOrganizerController.java |
startTransition() / finishTransition() / finishTransitionWithState() |
| Binder 接口定义 | ITransitionPlayer.aidl |
requestStartTransition() / onTransitionReady() |
附录:完整调用链示例(点击 Launcher 图标启动 Activity)
scss
Launcher.onClick()
→ startActivity(intent)
→ ActivityTaskManagerService.startActivityAsUser()
→ ActivityStack.startActivityLocked()
→ ActivityRecord.setVisible(true)
→ mTransitionController.collectVisibleChange(this) ← 收集可见性变化
→ mTransitionController.collectExistenceChange(this) ← 收集存在性变化
→ mTransitionController.getCollectingTransition().collect(wc)
→ ActivityStack.resumeTopActivityInnerLocked()
→ mTransitionController.requestTransitionIfNeeded(TRANSIT_OPEN, ...)
→ createTransition(type, flags)
→ mTransitionController.moveToCollecting(transition) ← 进入收集状态
→ requestStartTransition(transition, startTask, ...)
→ mPlayer.requestStartTransition(token, request) ← 通知 Shell (Binder)
Shell.Transitions.requestStartTransition() ← 以下三步在同一方法内同步完成
→ dispatchRequestWithTracing() → Handler 链认领
→ mDisplayController.onDisplayChangeRequested() (如旋转)
→ mOrganizer.startTransition(token, wct) ← 同步回告 WMS (Binder)
WindowOrganizerController.startTransition()
→ 通过 token 找到 Transition
→ transition.start() ← STATE_COLLECTING → STATE_STARTED
→ applyTransaction(wct) ← 应用 Shell 返回的 WCT
[WMS 等待所有窗口绘制完成]
→ ActivityRecord.performTraversals() → 新 Activity 绘制
→ WindowState.finishDrawing(postDrawT)
→ onSyncFinishedDrawing()
→ BLASTSyncEngine.onSurfacePlacement()
→ tryFinish() → 所有成员就绪
→ finishNow()
→ finishSync() 递归合并所有 mSyncTransaction
→ merged.merge(mOrphanTransaction)
→ mListener.onTransactionReady(syncId, merged)
Transition.onTransactionReady()
→ commitVisibleActivities(transaction)
→ mTargets = calculateTargets(...)
→ TransitionInfo info = calculateTransitionInfo(...)
→ mController.assignTrack(this, info)
→ mController.moveToPlaying(this)
→ mController.getTransitionPlayer().onTransitionReady(token, info, startT, finishT) ← 通知 Shell
Shell.Transitions.onTransitionReady()
→ dispatchReady() → playTransition()
→ DefaultTransitionHandler.startAnimation(token, info, startT, finishT, onFinish)
→ DefaultSurfaceAnimator.buildSurfaceAnimation()
→ ValueAnimator 逐帧提交 Surface 变换
[动画播放完成]
→ Shell.onFinish(token, wct)
→ mOrganizer.finishTransitionWithState(token, wct, null, state) ← 回告 WMS
WindowOrganizerController.finishTransitionWithState()
→ TransitionController.finishTransition(chain)
→ Transition.finishTransition(chain)
→ 从 mPlayingTransitions 移除
→ 提交不可见容器、清理 starting window、通知监听器