动画、切换与窗口移除如何保持一致
动画、切换与窗口移除如何保持一致
应用请求移除窗口后,逻辑状态、退出动画、Surface 事务和 WindowState 销毁通常不会在同一 时刻完成。removeIfPossible() 允许系统等待动画或 Transition 收尾,removeImmediately() 才执行最终清理;把两者混为一步会误判“窗口已经退出但画面仍在”的现场。
逻辑退出与画面消失不是同一个时刻
| 常见误解 | 正确理解 |
|---|---|
| 应用关闭窗口,系统立刻销毁一切 | WMS 常先跑退出动画,动画结束才真正移除对象 |
| 窗口在逻辑上退出了,画面马上消失 | 视觉画面由 SurfaceFlinger 按事务更新,和逻辑状态不同步 |
为了让退出动画平滑,系统把“逻辑上要移除”和“实际上销毁”拆成两步:先标记、再等条件满足。这个设计避免了动画过程中对象已被销毁导致的闪烁。
移除的两条路径
逐步解释:
- ① 入口:应用调用
Session.remove(),WMS 的removeClientToken()找到窗口(WindowManagerService.java:1983)。 - ② 判断:
removeIfPossible()(WindowState.java:2360)检查是否可立即移除。 - ③ 延迟分支:窗口有 Surface 且可动画时,标记
mAnimatingExit并调用setupWindowForRemoveOnExit()(同文件 2469 / 2501 行),等动画完成。 - ④ 立即分支:没有动画需求时直接
removeImmediately()(同文件 2298 行)。 - ⑤ 真正销毁:从
mWindowMap移除、销毁 Surface 与输入通道。
为什么“移除”分两步
removeIfPossible() 的名字已经说明一切:它先“尽可能”判断,必要时推迟。推迟期间窗口仍在系统状态里,但被标记为退出,不再参与焦点和输入命中。removeImmediately() 才是不可逆的销毁动作。
销毁动作同时清理三样东西:mWindowMap 里的窗口记录、SurfaceController 持有的 Layer、以及打开过的输入通道。清理顺序影响正确性:先摘除状态映射,再销毁 Surface,最后释放输入资源,避免移除过程中残留的输入窗口信息指向已销毁对象。
// WindowState.java:2360
void removeIfPossible() {
mWindowRemovalAllowed = true;// WindowState.java:2480
removeImmediately();这段代码证明: removeIfPossible() 在条件满足时调用 removeImmediately(),两条路径在销毁动作上汇合。
Transition 与 Surface 事务
现代 Android 用 Shell Transition 统一编排动画;mTransitionController 也会参与焦点保持。 WMS 负责逻辑终态和接口,视觉事务由 Transition/WM Shell 编排,最终通过 SurfaceControl 事务到达 SurfaceFlinger。这部分的内部状态机见SurfaceControl 与 Transition和WM Shell。
源码入口
源码基准:android-14.0.0_r75。
以下文件默认位于 frameworks/base/services/core/java/com/android/server/wm/,表中省略该前缀:
| 文件 | 关键位置 | 作用 |
|---|---|---|
WindowManagerService.java | 1983 行 removeClientToken();1989 行 win.removeIfPossible();2007 行 mWindowMap.remove(...) | 移除入口与映射清理 |
WindowState.java | 2298 行 removeImmediately();2360 行 removeIfPossible();2469 / 2501 行 setupWindowForRemoveOnExit();2480 行 removeImmediately() | 延迟与立即移除 |
WindowState.java | 1483 行 mTransitionController.isShellTransitionsEnabled() | Transition 边界 |
| 关联专题 | surface-control-transitions.md / wm-shell.md | Transition 与 Shell 内部 |