Android ShellTransitions 机制完整分析
一、系统架构总览
ShellTransitions 是一个三层架构 ,将转场动画的生命周期管理(WMCore)、窗口同步(BLASTSyncEngine)与动画执行(WMShell)完全分离。核心思想是:WMCore管理"发生了什么变化",BLAST保证"窗口准备好了",WMShell负责"怎么动画展示"。
1.1 架构分层图
scss
┌──────────────────────────────────────────────────────────────────────┐
│ 外部调用者 │
│ ┌──────────┐ ┌──────────┐ ┌───────────┐ ┌───────────────────┐ │
│ │Launcher3 │ │ SystemUI │ │ 第三方App │ │ ActivityStarter │ │
│ │(手势导航) │ │(锁屏状态栏)│ │(远程动画) │ │(Activity启动) │ │
│ └────┬─────┘ └────┬─────┘ └─────┬──────┘ └────────┬──────────┘ │
│ │ │ │ │ │
│ │ IShellTransitions (AIDL) │ WindowOrganizerController│
│ └──────────────┼──────────────┘ │ │
│ │ │ │
└──────────────────────┼─────────────────────────────────┼──────────────┘
│ │
┌──────────────────────┼─────────────────────────────────┼──────────────┐
│ │ WMCore Layer (Server) │ │
│ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ TransitionController │ │
│ │ • 全局转场生命周期管理 │ │
│ │ • mCollectingTransition (当前收集中) │ │
│ │ • mWaitingTransitions (等待就绪队列) │ │
│ │ • mPlayingTransitions (播放中) │ │
│ │ • mQueuedTransitions (排队中) │ │
│ │ • assignTrack() 轨道分配 │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼──────────────────────────────────────┐ │
│ │ Transition │ │
│ │ • 状态机: PENDING→COLLECTING→STARTED→PLAYING→FINISHED │ │
│ │ • mParticipants: 参与者集合 │ │
│ │ • mChanges: 每个参与者的 ChangeInfo │ │
│ │ • 与 BLASTSyncEngine 协调同步 │ │
│ └──────────────────────────┬──────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────────▼──────────────────────────────────────┐ │
│ │ BLASTSyncEngine │ │
│ │ • SyncGroup: 同步组管理 │ │
│ │ • WindowContainer同步状态: │ │
│ │ NONE→WAITING_FOR_DRAW→READY │ │
│ │ • Transaction合并与提交 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ 通信接口: ITransitionPlayer (Binder IPC) │
└───────────────────────────────────────────────────────────────────────┘
│
│ ITransitionPlayer.aidl
│
┌──────────────────────────────┼───────────────────────────────────────┐
│ │ WMShell Layer (Client) │
│ ┌───────────────────────────▼─────────────────────────────────────┐ │
│ │ Transitions │ │
│ │ (ITransitionPlayer.Stub 实现, 转场播放器) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ 生命周期管理 │ │ │
│ │ │ mPendingTransitions → READY → (Track队列) │ │ │
│ │ │ mKnownTransitions (全局状态追踪) │ │ │
│ │ │ mReadyDuringSync (SYNC阻塞等待队列) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ 动画执行 │ │ │
│ │ │ setupStartState() 设置初始状态 │ │ │
│ │ │ setupAnimHierarchy() 建立动画层级 │ │ │
│ │ │ calculateAnimLayer() 计算Z轴顺序 │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ Handler责任链 │ │ │
│ │ │ (高优先级) RemoteTransitionHandler │ │ │
│ │ │ KeyguardTransitionHandler │ │ │
│ │ │ PipTransitionHandler │ │ │
│ │ │ SplitScreenTransitionHandler │ │ │
│ │ │ (低优先级) DefaultTransitionHandler (兜底默认) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ 轨道系统 │ │ │
│ │ │ Track[0]: mReadyTransitions → mActiveTransition │ │ │
│ │ │ Track[1]: mReadyTransitions → mActiveTransition │ │ │
│ │ │ Track[N]: (并行播放) │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 对外接口: IShellTransitions (AIDL) │ │
│ │ registerRemote() / registerRemoteForTakeover() │ │
│ │ setHomeTransitionListener() / setFocusTransitionListener() │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────┘

二、BLAST同步机制详解
BLAST (Buffer as LayerSync Transaction) 同步机制是 ShellTransitions 能够在正确时机播放动画的基础保证。它的核心目标是:确保在窗口内容已经绘制完毕之后,才开始播放转场动画。
2.1 问题背景
在Android窗口系统中,WM端的状态变更(如配置变化、可见性变化)会触发客户端重绘。如果不等客户端绘制完成就开始动画,会出现两个问题:
- 白屏/闪烁:动画开始时窗口还没有新内容
- 视觉跳跃:旧内容随着动画变换,新内容突然出现
2.2 核心保证
如果同时对同步状态进行变更和启动同步,那么客户端在观察到同步状态后绘制的第一帧,将被发送到同步的消费者(而非直接发送给SurfaceFlinger)。
2.3 协议定义
BLASTSyncEngine 定义了完整的同步协议:
Server协议
makefile
SA: 进入临界区(获取WM锁)
SB: 变更可同步状态,准备任意数量的同步(递增seqId)
SC: 离开临界区(释放WM锁)
SD: 向客户端发送同步状态更新,始终携带当前seqId
SE: 当客户端调用 finishDrawing 时,对所有 seqId ≤ 传递seqId 的同步执行消费者回调
Client协议
makefile
CA: 观察状态和seqId变更直到固定帧截止时间,然后执行 performTraversals
CB: 如果seqId在帧截止时递增,配置渲染器将下一次绘制重定向到Transaction,记录当前seqId
CC: 绘制完成后,将包含绘制的Transaction连同记录的seqId发送给WM
2.4 同步状态机
css
每个 WindowContainer 都有 mSyncState:
┌──────────────────┐
│ SYNC_STATE_NONE │ (0) 未参与同步
└────────┬─────────┘
│ prepareSync() --- 加入SyncGroup
┌────────▼─────────┐
│ SYNC_STATE_READY │ (2) 内容最新,直接可用
└────────┬─────────┘
│ resize/relayout --- 发生布局变化
┌────────▼──────────────┐
│ SYNC_STATE_WAITING │ (1) 等待客户端重绘
│ _FOR_DRAW │
└────────┬───────────────┘
│ finishDrawing() --- 客户端绘制完成
┌────────▼─────────┐
│ SYNC_STATE_READY │ (2) 回到就绪
└──────────────────┘
2.5 SyncGroup 生命周期
scss
┌──────────────────────────────────────────────────────────────────────┐
│ SyncGroup 生命周期 │
│ │
│ 1. 创建 2. 添加成员 3. 等待就绪 │
│ prepareSyncSet() addToSyncSet(wc) mReady = true │
│ ┌──────────┐ ┌──────────────┐ ┌──────────┐ │
│ │ SyncGroup│─────────►│ wc.prepareSync│──────────►│ 等待所有 │ │
│ │ (未激活) │ │ 遍历子树 │ │ 成员就绪 │ │
│ └──────────┘ └──────────────┘ └────┬─────┘ │
│ │ │
│ 6. 清理 5. 完成 │ │
│ finishSync() onTransactionReady() ◄────────┘ │
│ ┌──────────┐ ┌──────────────┐ │
│ │ 恢复 │◄────────│ tryFinish() │ │
│ │ pendingTr │ │ isSyncFinished│ │
│ └──────────┘ │ 检查所有成员 │ │
│ └──────────────┘ │
│ │
│ 关键方法: │
│ • tryFinish(): 检查 mReady && 无依赖 && 所有 rootMembers isSyncFinished│
│ • finishNow(): 合并所有 SyncTransaction → 调用 listener.onTransactionReady│
│ • onSurfacePlacement(): 遍历所有活跃SyncGroup尝试完成 │
└──────────────────────────────────────────────────────────────────────┘
2.6 BLAST 与 ShellTransitions 的集成
scss
Transition.startCollecting()
│
▼
BLASTSyncEngine.startSyncSet(transition, timeout, "Transition")
│ 创建 SyncGroup,Transition 作为 TransactionReadyListener
▼
TransitionController.collect(wc)
│
▼
Transition.collect(wc)
│
▼
SyncGroup.addToSync(wc)
│ wc.prepareSync() → mSyncState = READY
│ 如果发生 resize → mSyncState = WAITING_FOR_DRAW
▼
WindowState.finishDrawing(transaction)
│ 客户端绘制完成
│ mSyncState = READY
▼
BLASTSyncEngine.onSurfacePlacement()
│
▼
SyncGroup.tryFinish()
│ isSyncFinished() 递归检查所有成员
│ 所有成员 READY → finishNow()
▼
Transition.onTransactionReady(mergedSyncTransaction)
│ Transition 获得所有参与者的合并SyncTransaction
│ 调用 Transition.playNow()
▼
构建 TransitionInfo → ITransitionPlayer.onTransitionReady()
↓
WMShell 开始动画
2.7 Transaction 排序保证
BLAST同步同时解决了跨进程Transaction的顺序问题:
rust
BLASTBufferQueue 的 Transaction 类型:
┌──────────────┬──────────────────────────────────────────────┐
│ 类型 │ 排序保证 │
├──────────────┼──────────────────────────────────────────────┤
│ Sync,Sync │ 委托给 SyncConsumer │
│ Sync,NotSync │ CommitCallback 保证 Sync 先被应用 │
│ NotSync,Sync │ Barrier: Sync标记最后一帧NotSync barrier │
│ NotSync,NotSync │ 同线程同队列保证顺序 │
└──────────────┴──────────────────────────────────────────────┘

三、轨道机制 (Track) 详解
轨道机制是 ShellTransitions 实现并行动画 与串行动画有序混合的核心设计。
3.1 设计动机
在复杂的多窗口场景中,不同的转场可能涉及完全不同的窗口:
- 分屏场景:上半屏一个动画,下半屏另一个动画 --- 应该并行
- 同一Task的连续操作:打开一个Activity,紧接着它又被缩小 --- 应该串行
- 跨多个轨道的转场:一个转场涉及分屏两边 --- 必须等待所有轨道清空
轨道机制优雅地解决了这个三维调度问题。
3.2 核心数据结构
ini
Transitions (WMShell) TransitionController (WMCore)
┌──────────────────────────┐ ┌──────────────────────────────┐
│ mTracks: ArrayList<Track>│ │ mPlayingTransitions[] │
│ │ │ mTrackCount │
│ Track[0]: │ │ │
│ mActiveTransition ─────┼────────────┼─ 当前播放的Transition │
│ mReadyTransitions[] │ │ .mAnimationTrack = 0 │
│ │ │ │
│ Track[1]: │ │ assignTrack(): │
│ mActiveTransition ─────┼────────────┼─ 与Track[0]独立 → Track[1] │
│ mReadyTransitions[] │ │ 与Track[0]重叠 → Track[0] │
│ │ │ 与多个Track重叠 → SYNC │
│ Track[N] 动态创建 │ │ │
└──────────────────────────┘ └──────────────────────────────┘
3.3 轨道分配算法 (WMCore: assignTrack)
ini
assignTrack(transition, info):
track = -1
sync = false
for each playing in mPlayingTransitions:
if playing == transition: continue
if getIsIndependent(playing, transition): continue
if track == -1:
track = playing.mAnimationTrack // 加入相同轨道
else if track != playing.mAnimationTrack:
sync = true // 与多轨道重叠!
break
if sync:
track = 0 // 强制归入轨道0
info.setFlags(FLAG_SYNC) // 标记同步
if track < 0:
track = mTrackCount++ // 新轨道(并行)
return track
3.4 独立性判断 (getIsIndependent)
两个Transition可以并行的条件:
scss
Transition A (播放中) Transition B (新进入)
│ │
├─ 目标窗口是 Activity 级别的? ───────┤ (如Task级别则不可)
│ │
├─ 不是 A 的 transient-launch ────────┤ (重叠则不可)
│ 目标? │
│ │
└─ 在不同Display? ────────────────────┤ (不同屏可以并行)
具体规则:
- Recents转场 + Recents转场 → 必须串行(自己不能和自己并行)
- Recents转场 + 非Recents转场 → 检查窗口是否重叠
- Activity级 + 不重叠 → 可以并行
- Task级 → 目前不允许并行
3.5 Shell端轨道执行
ini
每个 Track 内部:
┌─────────────────────────────────────────────────────┐
│ Track 工作流程 │
│ │
│ mReadyTransitions: [T3, T2, T1] (FIFO队列) │
│ │
│ processReadyQueue(): │
│ ┌─ mActiveTransition == null? │
│ │ YES → T1出队 → 设为mActiveTransition │
│ │ → playTransition(T1) │
│ │ → 尝试合并 T2 → T1 (如成功则T2 = MERGED) │
│ │ → 尝试合并 T3 → T1 ... │
│ │ │
│ │ NO → mergeAnimation(T1, mActiveTransition) │
│ │ → 成功: T1被合并, 从队列移除 │
│ │ → 失败: T1留在队列等待 │
│ │ │
│ onFinish(): │
│ → mActiveTransition = null │
│ → processReadyQueue() (启动下一个) │
└─────────────────────────────────────────────────────┘
多个 Track 之间:
Track[0]: T0_active → T0_ready1 → T0_ready2 (串行)
Track[1]: T1_active → T1_ready1 (串行, 与Track[0]并行)
FLAG_SYNC 转场到达时:
→ 所有Track必须排空
→ mReadyDuringSync 收集被阻塞的转场
→ 所有Track idle后释放
3.6 SYNC 机制
当转场与多个活跃轨道重叠时:
scss
1. 转场被标记 FLAG_SYNC,加入 mReadyDuringSync 队首
2. 对每个活跃轨道调用 finishForSync():
┌─ 向正在播放的动画 merge 一个虚拟 SLEEP 转场(信号)
┌─ 设置 120ms 超时
┌─ 超时后强制 terminate 仍未完成的动画
┌─ 重复直到该轨道 idle
3. 所有轨道 idle 后,mReadyDuringSync 中的转场被释放执行
四、Merge 机制详解
Merge机制允许新到达的转场无缝合并到正在播放的动画中,优化了连续操作的视觉体验。
4.1 触发时机
css
时刻线:
Track[0]: [T1_active ~~~~~~ 播放中 ~~~~~~→]
此时 T2 到达 (ready状态)
processReadyQueue(Track[0]):
检测到 mActiveTransition(T1) != null
→ 调用 T1.mHandler.mergeAnimation(T2, ..., T1_token, callback)
4.2 Handler的三种策略
css
┌──────────────────────────────────────────────────────────────────┐
│ Merge 三种响应策略 │
│ │
│ 策略1: 拒绝 (No-op) │
│ ───────────────── │
│ mergeAnimation() 不做任何操作 │
│ → T2 留在 track.mReadyTransitions 等待 │
│ → T1 完成后 T2 独立播放 │
│ 适用: 转场类型不兼容 (如 PiP打开 vs 全屏关闭) │
│ │
│ 策略2: 接受并合并 (Accept & Merge) │
│ ─────────────────────────────── │
│ mergeAnimation() 内部调用 finishCallback │
│ → T2 的 Change 合并到 T1 的 ActiveTransition.mMerged[] │
│ → T2 的 startTransaction 和 finishTransaction 被纳入 T1 │
│ → T1 结束时一起 apply │
│ 适用: 同一窗口的连续变化 (如 打开Activity → Activity变换) │
│ │
│ 策略3: 终止当前并覆盖 (End & Replace) │
│ ────────────────────────────────── │
│ mergeAnimation() 先终止 T1 的动画 (anim.end()) │
│ → 然后 T2 作为新的播放开始 │
│ DefaultTransitionHandler 采用此策略 │
│ 适用: 后来的转场完全取代之前的 (如 用户快速连续操作) │
└──────────────────────────────────────────────────────────────────┘
4.3 DefaultTransitionHandler 的 Merge 实现
scss
DefaultTransitionHandler.mergeAnimation():
1. 找到 mergeTarget(被合并目标) 的 Animator 列表
ArrayList<Animator> animators = mAnimations.get(mergeTarget)
2. 对所有正在运行的 Animator 调用 anim.end()
→ 强制动画跳到最终状态
3. 返回 (不调用 finishCallback)
效果:
- 旧动画被截断到最终状态
- 新转场作为独立动画在 processReadyQueue 中播放
- 新转场的 startTransaction 包含修正后的状态
4.4 Transitions 层的 Merge 清理
scss
Transitions.onMerged(playingToken, mergedToken):
playing = mKnownTransitions.get(playingToken)
merged = mKnownTransitions.get(mergedToken)
1. 验证: 同一轨道, merged 在队列中
2. 从 track.mReadyTransitions 中移除 merged
3. 将 merged 加入 playing.mMerged[]
4. 如果 merged 未被abort:
merged.mHandler.onTransitionConsumed(merged.mToken, false, ...)
5. 通知所有 TransitionObserver.onTransitionMerged()
6. 继续 processReadyQueue() (尝试合并下一个)
4.5 结束时的 Merge 回放
scss
Transitions.onFinish(token, wct):
active = mKnownTransitions.get(token)
// 收集所有被合并转场的 Transaction
fullFinish = active.mFinishT
for each merged in active.mMerged:
fullFinish.merge(merged.mStartT) // StartT: 无操作则跳过
fullFinish.merge(merged.mFinishT)
// 一次性 apply 所有变更
fullFinish.apply()
// 逐个通知 WMCore 转场结束
mOrganizer.finishTransition(active.mToken, wct)
for each merged in active.mMerged:
mOrganizer.finishTransition(merged.mToken, null)
releaseSurfaces(merged.mInfo)
五、完整流程序列图
5.1 主流程
scss
┌─────────┐ ┌──────────────┐ ┌──────────┐ ┌───────────────┐ ┌───────────┐ ┌─────────────┐
│ 调用方 │ │TransitionCtrl│ │Transition│ │BLASTSyncEngine│ │Transitions│ │Transition │
│ │ │ │ │ │ │ │ │(WMShell) │ │Handler │
└────┬─────┘ └──────┬───────┘ └────┬─────┘ └───────┬───────┘ └─────┬─────┘ └──────┬──────┘
│ │ │ │ │ │
│ createTrans() │ │ │ │ │
│──────────────►│ │ │ │ │
│ │ new Transition│ │ │ │
│ │──────────────►│ │ │ │
│ │ │ │ │ │
│ │moveToCollect()│ │ │ │
│ │──────────────►│ STATE=COLLECTING│ │ │
│ │ │ │ │ │
│ │requestStart() │ │ │ │
│ │──────────────►│ build RequestInfo│ │ │
│ │ │─────────────────────────────────► │
│ │ │ IPC: requestStartTransition │ │
│ │ │ │ │ │
│ │ │ │ 创建 │ │
│ │ │ │ ActiveTransition│ │
│ │ │ │ │ │
│ │ │ │dispatchRequest │ │
│ │ │ │───────────────►│ handleRequest │
│ │ │ │ │ (返回WCT) │
│ │ │ │◄───────────────│ │
│ │ │ │ │ │
│ │ │◄─────────────────────────────────│ │
│ │ │ IPC: startTransition(token,wct) │
│ │ │ │ │ │
│ │ │ │ mPendingTrans. │ │
│ │ │ │ .add(0,active) │ │
│ │ │ │ │ │
════════════════ 阶段2: 参与者收集 ════════════════ │ │
│ │ │ │ │ │
│ collect(wc) │ │ │ │ │
│──────────────►│ │ │ │ │
│ │collect(wc) │ │ │ │
│ │──────────────►│ addToSync(wc) │ │ │
│ │ │────────────────►│ │ │
│ │ │ │ wc.prepareSync │ │
│ │ │ │ STATE=READY │ │
│ │ │ │ │ │
│ │ │ resize → STATE=WAITING_FOR_DRAW │
│ │ │ │ │ │
│ │ │ finishDrawing │ │ │
│ │ │◄────────────────│ │ │
│ │ │ STATE=READY │ │ │
│ │ │ │ │ │
════════════════ 阶段3: 就绪通知 ════════════════ │ │
│ │ │ │ │ │
│ │ │onSurfacePlacement() │ │
│ │ │◄────────────────│ │ │
│ │ │ tryFinish() │ │ │
│ │ │────────────────►│ │ │
│ │ │ │ │ │
│ │ │onTransactionReady(mergedT) │ │
│ │ │◄────────────────│ │ │
│ │ │ │ │ │
│ │ │ playNow() │ │ │
│ │ │ build TransitionInfo │ │
│ │ │ assignTrack() │ │ │
│ │◄──────────────│ │ │ │
│ │ │ │ │ │
│ │ │─────────────────────────────────►│ │
│ │ │ IPC: onTransitionReady │ │
│ │ │ │ │ │
════════════════ 阶段4: 动画执行 ════════════════ │ │
│ │ │ │ │ │
│ │ │ │PENDING→READY │ │
│ │ │ │dispatchReady() │ │
│ │ │ │ │ │
│ │ │ │setupStartState │ │
│ │ │ │ │ │
│ │ │ │加入Track队列 │ │
│ │ │ │ │ │
│ │ │ │processReadyQ() │ │
│ │ │ │ │ │
│ │ │ │setupAnimHierarchy() │
│ │ │ │ (reparent+Z序) │ │
│ │ │ │ │ │
│ │ │ │dispatchTransition() │
│ │ │ │───────────────────────────────►│
│ │ │ │ startAnimation() │
│ │ │ │ │ │
│ │ │ │ startT.apply() │ │
│ │ │ │────────────────► SF │
│ │ │ │ │ │
════════════════ 阶段5: 动画结束 ════════════════ │ │
│ │ │ │ │ │
│ │ │ │finishCallback │ │
│ │ │ │◄───────────────│ │
│ │ │ │ │ │
│ │ │ │onFinish() │ │
│ │ │ │ │ │
│ │ │ │merge all finishT │
│ │ │ │fullFinish.apply() │
│ │ │ │────────────────► SF │
│ │ │ │ │ │
│ │ │◄─────────────────────────────────│ │
│ │ │ IPC: finishTransition(token,wct) │
│ │ │ │ │ │
│ │ │finishTransition │ │ │
│ │◄──────────────│ │ │ │
│ │ │ │ │ │
│ │ │validateStates() │ │ │
│ │ │ │ │ │
│ │ │ │processReadyQ() │ │
│ │ │ │(下一个转场) │ │
5.2 Merge 流程
less
Track[0] 状态:
mActiveTransition = T1 (正在播放)
mReadyTransitions = [T2, T3] (等待队列)
════════════ T2 Merge 尝试 ════════════
processReadyQueue(Track[0]):
T2 = track.mReadyTransitions.get(0) // 队首
T2.mAborted? → NO → 进入 Merge 流程
T1.mHandler.mergeAnimation(
T2.mToken, // 新转场
T2.mInfo, // 新转场信息
T2.mStartT, // 新转场的start Transaction
T2.mFinishT, // 新转场的finish Transaction
T1.mToken, // 合并目标
callback // 成功回调 → onMerged(T1_token, T2_token)
)
分支A: Merge 失败 (handler 不调用 callback)
─────────────────────────────────────
T2 留在 mReadyTransitions
等待 T1 结束后独立播放
分支B: Merge 成功 (handler 调用 callback)
─────────────────────────────────────
onMerged(T1_token, T2_token):
1. 从 track.mReadyTransitions 移除 T2
2. T2 加入 T1.mMerged[]
3. T2.mHandler.onTransitionConsumed(T2_token, false, T2.mFinishT)
4. 通知 TransitionObservers
继续尝试合并 T3:
processReadyQueue() → T3 成为新的队首
→ mergeAnimation(T3, T1_token, callback)
六、状态机总览
6.1 WMCore端 Transition 状态
ini
┌──────────────────────────────┐
│ STATE_PENDING = -1 │
│ 已创建,未开始收集 │
└────────────┬─────────────────┘
│ moveToCollecting()
┌────────────▼─────────────────┐
│ STATE_COLLECTING = 0 │
│ 正在收集中 (可并行收集) │
└────────────┬─────────────────┘
│ startCollecting(timeout)
┌────────────▼─────────────────┐
│ STATE_STARTED = 1 │
│ 正式启动,等待参与者就绪 │
│ BLASTSync 确保窗口绘制完成 │
└────────────┬─────────────────┘
│ playNow()
┌────────────▼─────────────────┐
│ STATE_PLAYING = 2 │
│ 动画播放中 (在WMShell执行) │
└────────────┬─────────────────┘
│ finishTransition()
┌────────────▼─────────────────┐
│ STATE_FINISHED = 4 │
│ 转场完成 │
└──────────────────────────────┘
任意阶段 → STATE_ABORT = 3 (中止)
6.2 WMShell端 ActiveTransition 流转
scss
┌─────────────────────────────────────────────────────────────────┐
│ ActiveTransition 生命周期 │
│ │
│ requestStartTransition() │
│ │ │
│ ▼ │
│ ┌─────────────┐ │
│ │ PENDING │ mPendingTransitions │
│ │ (等待就绪) │ │
│ └──────┬──────┘ │
│ │ onTransitionReady() │
│ ▼ │
│ ┌─────────────┐ │
│ │ READY │ track.mReadyTransitions │
│ │ (就绪排队) │ │
│ └──┬──────┬───┘ │
│ │ │ │
│ │ └────────── mergeAnimation() ──────┐ │
│ │ processReadyQueue() │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ ACTIVE │ │ MERGED │ │
│ │ (动画播放中)│◄─────────────────────│ (被合并) │ │
│ └──────┬──────┘ merged[] └──────────────┘ │
│ │ │
│ │ onFinish() (finishCallback) │
│ ▼ │
│ ┌─────────────┐ │
│ │ 完成 │ apply finishT → finishTransition IPC │
│ │ 清理 │ 释放 surfaces → 启动队列下一个 │
│ └─────────────┘ │
│ │
│ 特殊情况: onAbort() → 跳过动画, 直接走完成流程 │
│ SYNC标志 → 进入 mReadyDuringSync 等待所有轨道排空 │
└─────────────────────────────────────────────────────────────────┘
七、动画层级计算
7.1 Z轴分割线算法
erlang
calculateAnimLayer(change, index, numChanges, transitType):
zSplitLine = numChanges + 1 // 分割线
┌─────────────────────────────────────────┐
│ 分割线以上 (动画层) z > zSplitLine │
│ ┌───────────────────────────────────┐ │
│ │ OPEN / TO_FRONT (打开类型转场) │ │
│ │ CLOSE / TO_BACK (关闭类型转场) │ │
│ │ CHANGE (打开类型转场) │ │
│ └───────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ 分割线以下 (静态层) z < zSplitLine │
│ ┌───────────────────────────────────┐ │
│ │ OPEN / TO_FRONT (关闭类型转场) │ │
│ │ CLOSE / TO_BACK (打开类型转场) │ │
│ │ CHANGE (关闭类型 / 仅排序) │ │
│ └───────────────────────────────────┘ │
└─────────────────────────────────────────┘
具体 z 值 = zSplitLine + offset (上) 或 zSplitLine - offset (下)
7.2 动画层级树构建
ini
setupAnimHierarchy(info, startT, finishT):
原始层级: 动画层级(reparent后):
DisplayContent Root(leash)
├── Task A ├── Change[0] (OPEN, layer=high)
├── Task B ──► ├── Change[1] (CLOSE, layer=mid)
├── Wallpaper ├── Change[2] (CHANGE, layer=low)
└── ... └── ...
关键操作:
1. 所有参与窗口的leash被reparent到Root leash
2. 通过setLayer强制设定Z序
3. 非独立的子窗口不参与reparent(跟随父窗口)
4. Display级别的leash仅在非order-only变化时reparent
八、Handler 责任链
erlang
mHandlers (ArrayList) --- 顺序从低到高:
┌──────────────────────────────────────────────────────────────────┐
│ Index 0: DefaultTransitionHandler [最低优先级, 兜底] │
│ • handleRequest() → null (不干预请求阶段) │
│ • startAnimation() → 执行XML/Custom动画 │
│ • mergeAnimation() → anim.end() 终止旧动画 │
│ • 支持的动画类型: 所有 │
├──────────────────────────────────────────────────────────────────┤
│ Index 1: RemoteTransitionHandler [远程动画管理] │
│ • 维护 TransitionFilter → RemoteTransition 映射 │
│ • 匹配时委托给App进程执行动画 │
│ • 支持 takeover (动画接管) │
├──────────────────────────────────────────────────────────────────┤
│ Index 2+: (可选, 按添加顺序) │
│ KeyguardTransitionHandler [锁屏转场] │
│ PipTransitionHandler [画中画转场] │
│ SplitScreenTransitionHandler [分屏转场] │
│ DesktopModeTransitionHandler [桌面模式转场] │
│ ...更多自定义Handler... │
└──────────────────────────────────────────────────────────────────┘
dispatchTransition() 遍历逻辑:
for i = mHandlers.size()-1 down to 0: // 从高优先级到低
consumed = handler.startAnimation(...)
if consumed: return handler
throw IllegalStateException // 不应发生 (Default总是兜底)
九、关键类型总结
9.1 转场类型 (TransitionType)
| 类型 | 说明 |
|---|---|
TRANSIT_OPEN |
新窗口打开 |
TRANSIT_CLOSE |
窗口关闭 |
TRANSIT_TO_FRONT |
窗口移到前面 |
TRANSIT_TO_BACK |
窗口移到后面 |
TRANSIT_CHANGE |
窗口变化(resize等) |
TRANSIT_SLEEP |
休眠(触发强制同步) |
TRANSIT_KEYGUARD_OCCLUDE |
锁屏遮挡 |
| Shell自定义类型 | PIP/分屏/最大化/最小化/桌面模式等 |
9.2 Change Mode (参与者变化模式)
| Mode | 含义 |
|---|---|
TRANSIT_OPEN |
窗口从不可见变为可见 |
TRANSIT_CLOSE |
窗口从可见变为不可见 |
TRANSIT_TO_FRONT |
窗口提升到前面 |
TRANSIT_TO_BACK |
窗口降低到后面 |
TRANSIT_CHANGE |
窗口内容/边界变化 |
9.3 关键 Flag
| Flag | 含义 |
|---|---|
FLAG_SYNC |
需要等待所有轨道清空 |
FLAG_IS_DISPLAY |
参与者是Display级别 |
FLAG_IS_WALLPAPER |
参与者是壁纸 |
FLAG_IS_OCCLUDED |
窗口被其他窗口遮挡 |
FLAG_NO_ANIMATION |
不为此窗口播放动画 |
FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT |
接收StartingWindow转移 |
FLAG_MOVED_TO_TOP |
窗口移动到顶层 |
FLAG_IS_NON_APP_WINDOW |
非App窗口(系统窗口) |
十、性能与调试
10.1 超时保护
| 超时类型 | 时长 | 说明 |
|---|---|---|
| 收集超时 (DEFAULT) | 5000ms | 参与者收集的总时间 |
| 收集超时 (CHANGE) | 2000ms | CHANGE类转场预期更快 |
| Sync强制超时 | 120ms | Sleep/Sync信号后每轨最大等待 |
| BLAST Timeout | BLAST_TIMEOUT_DURATION | CommitCallback超时 |
10.2 性能提升机制
- 并行动画:通过Track机制支持多轨道同时播放
- 并行收集:当前收集完成后等待阶段,下一个转场可以开始收集
- 动画加速:Sleep转场可强制加速所有动画到最终状态
- Surface释放:动画结束后立即释放动画Surface(节省GPU内存)
- 进程优先级:远程动画执行期间提升动画进程优先级
- Snapshot暂停:动画期间暂停Task快照持久化(降低IO竞争)
10.3 Tracing支持
- Perfetto :
shell_transition.proto定义trace事件 - ProtoLog :
WM_SHELL_TRANSITIONS组,日志点在关键方法入口 - InteractionJankMonitor: 追踪任务间转场的CUJ延迟
- TransitionMetrics: 记录转场各阶段的时间消耗
10.4 Debug属性
| 属性 | 作用 |
|---|---|
persist.wm.debug.shell_transitions |
启用Shell转场 |
persist.wm.debug.shell_transit_rotate |
使用Shell转场处理旋转 |
persist.wm.debug.shell_transit_blast |
切换BLAST同步方法 |
persist.wm.debug.start_shell_transition |
打印转场启动堆栈 |
persist.wm.debug.finish_shell_transition |
打印转场完成堆栈 |
