WMS Handler 分流
WindowManagerService.H 不是 WMS 的“总循环”,也不是动画线程本身。Android 17 中,WMS.H 在 system_server 的 WMS Looper 上处理窗口超时、绘制通知、焦点和配置等消息;窗口动画则由 WindowAnimator 使用 AnimationThread 的 Handler 和 Choreographer,Surface 动画由 SurfaceAnimationRunner 使用 SurfaceAnimationThread 的 Handler。不同 Handler 的真正差异 是 Looper 和锁边界,而不是消息常量数量。
本文面向已经读过 AMS Handler 分流、FrameHandler 帧调度、 ViewRootHandler 绘制路径 和 HandlerThread 生命周期 的读者。文章选择两条可复查路径:WMS 如何等待窗口绘制并在成功/超时后完成回调,以及动画 如何从 WMS 状态进入 AnimationThread/SurfaceAnimationThread。本文不展开 WMS 的全部窗口层级 算法、SurfaceFlinger 服务实现或每个 H 常量。
读完后,读者应能定位 WMS.H 的 Looper owner,解释 WAITING_FOR_DRAWN_TIMEOUT 为什么既清理 窗口集合又把 callback 放到锁外,判断动画调度为何不持有 WMS 全局锁,以及在窗口未绘制、动画 取消或线程退出时哪些状态仍需要显式清理。
1. Handler 所有权
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
WMS 创建一个内部 H,并保存 WindowAnimator、SurfaceAnimationRunner 等协作者。H 的 具体 Looper 来自 WMS 所在 system_server 线程;动画协作者可以绑定其它专用线程。
final H mH = new H();
final WindowAnimator mAnimator;
SurfaceAnimationRunner mSurfaceAnimationRunner;H 的消息 case 大多在 mGlobalLock 下读取或更新窗口状态,但并不是每个动作都在锁内完成。 例如等待绘制超时会在锁内提取 callback,再在锁外发送;动画则通过独立线程减少持有 WMS 锁的 时间。
| 对象 | Looper/线程 | 主要状态 | 典型消费者 |
|---|---|---|---|
WMS.H | WMS 主 Looper | 窗口、焦点、超时、显示状态 | WindowManagerService/DisplayContent |
WindowAnimator | AnimationThread + Choreographer | 窗口动画帧 | animate() |
SurfaceAnimationRunner | AnimationThread/SurfaceAnimationThread | SurfaceControl 动画 | applyTransaction() |
mGlobalLock | 共享锁 | WMS 全局状态一致性 | 上述多个消费者 |
2. 消息入口
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
WMS.H 是普通 Handler 子类,整数 what 表示协议,msg.obj 载荷保存具体 WindowContainer、 ActivityRecord 或回调 Message。本文只列与主线直接相关的几类:
final class H extends android.os.Handler {
public static final int REPORT_WINDOWS_CHANGE = 19;
public static final int BOOT_TIMEOUT = 23;
public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
public static final int UPDATE_ANIMATION_SCALE = 51;
public static final int WINDOW_HIDE_TIMEOUT = 52;
public static final int RECOMPUTE_FOCUS = 61;
public static final int WINDOW_STATE_BLAST_SYNC_TIMEOUT = 64;
}这些编号只在当前源码版本内有意义;读者应沿 handleMessage() 的 case 找到 payload 转换和 锁,而不是从旧版本记忆编号。
3. 等待绘制
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
WMS 需要等待一组窗口绘制完成时,把外部 callback 放入 mWaitingForDrawnCallbacks,同时向 mH 发送带 WindowContainer 的延迟超时消息。真正的等待集合由 container 持有。
container.waitForAllWindowsDrawn();
mWindowPlacerLocked.requestTraversal();
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
if (container.mWaitingForDrawn.isEmpty()) {
return true;
}
mWaitingForDrawnCallbacks.put(container, message);
mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
checkDrawnWindowsLocked();
return false;这里的 removeMessages 先清理同一 container 的旧超时,避免重复等待留下多个计时器。若 checkDrawnWindowsLocked() 发现窗口已经全部绘制,成功路径会移除超时并发送原 callback。
if (container.mWaitingForDrawn.isEmpty()) {
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
mWaitingForDrawnCallbacks.removeAt(i).sendToTarget();
}发送 callback 的动作发生在锁内的状态收束之后,但 callback 自身由其 target Handler 负责, 不等于 WMS 直接执行了调用方代码。
4. 超时消费
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
超时消息被 WMS.H 取出后,先在 mGlobalLock 内记录 timeout、结束窗口等待 trace、清空未绘制 集合并取出待通知 callback,然后在锁外发送 callback。
case WAITING_FOR_DRAWN_TIMEOUT: {
final Message callback;
final WindowContainer<?> container = (WindowContainer<?>) msg.obj;
synchronized (mGlobalLock) {
ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s",
container.mWaitingForDrawn);
for (int i = 0; i < container.mWaitingForDrawn.size(); i++) {
traceEndWaitingForWindowDrawn(container.mWaitingForDrawn.get(i));
}
container.mWaitingForDrawn.clear();
callback = mWaitingForDrawnCallbacks.remove(container);
}
if (callback != null) {
callback.sendToTarget();
}
break;
}这条路径的核心不变量是:无论成功绘制还是超时,mWaitingForDrawnCallbacks 都只保留一个 最终通知;窗口集合清空后,后续迟到的窗口绘制不能再次触发同一个等待 callback。
图中 callback 的最终执行线程由 callback 自身的 target Handler 决定;WMS 只负责状态和 触发时机。
5. 绘制确认
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
窗口绘制完成时,WMS 从 mWaitingForDrawn 移除对应 WindowState;集合为空才取消超时并发送 回调。这里的“成功”是等待集合收束,不是 SurfaceFlinger 已经完成所有显示提交。
container.mWaitingForDrawn.remove(win);
traceEndWaitingForWindowDrawn(win);
if (container.mWaitingForDrawn.isEmpty()) {
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
mWaitingForDrawnCallbacks.removeAt(i).sendToTarget();
}如果同一个 WindowState 的绘制通知重复到达,集合 remove 不会制造第二个 callback;如果窗口 被移除或超时,集合由超时路径清空。消息去重和集合状态共同形成一次性完成协议。
6. 其它超时
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
WMS.H 还处理 BOOT_TIMEOUT、WINDOW_HIDE_TIMEOUT、WALLPAPER_DRAW_PENDING_TIMEOUT 等消息。 它们共享“消息触发后重新检查状态”的模式,而不是把延迟时间本身当成事实。
case BOOT_TIMEOUT: {
performBootTimeout();
break;
}
case WALLPAPER_DRAW_PENDING_TIMEOUT: {
synchronized (mGlobalLock) {
final WallpaperController wallpaperController =
(WallpaperController) msg.obj;
if (wallpaperController != null
&& wallpaperController.processWallpaperDrawPendingTimeout()) {
mWindowPlacerLocked.performSurfacePlacement();
}
}
break;
}超时消息只提供一次检查机会。消费者返回什么、是否继续布局、是否通知外部对象,都由具体 WindowContainer/Controller 的状态决定。
7. 动画归属
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java
WindowAnimator 在 AnimationThread 的 Looper 上创建 Choreographer,使用 HandlerExecutor 复用 同一 Handler。WMS 的 scheduleAnimationLocked() 只调用 animator,不直接在 WMS.H 中推进每 个动画属性。
WindowAnimator(final WindowManagerService service) {
mService = service;
mAnimationHandler = service.mAnimationHandler;
service.mAnimationHandler.runWithScissors(
() -> mChoreographer = Choreographer.getInstance(), 0 /* timeout */);
mExecutor = new HandlerExecutor(service.mAnimationHandler);
}
void scheduleAnimation() {
if (!mAnimationFrameCallbackScheduled) {
mAnimationFrameCallbackScheduled = true;
mChoreographer.postVsyncCallback(mAnimationVsyncCallback);
}
}mAnimationFrameCallbackScheduled 是 animator 自己的去重状态。Choreographer 只提供 VSYNC 阶段,WMS.H 不会因为 scheduleAnimationLocked() 被调用就立即执行 animate()。
8. 动画帧
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java
VSYNC callback 到达 AnimationThread 后,先清除预约标志,再在 mGlobalLock 下调用 animate(frameTimeNanos)。动画器会先预约下一帧,再更新窗口和 Surface 状态。
mAnimationVsyncCallback = frameData -> {
synchronized (mService.mGlobalLock) {
mAnimationFrameCallbackScheduled = false;
animate(frameData.getFrameTimeNanos());
if (mNotifyWhenNoAnimation && !mLastRootAnimating) {
mService.mGlobalLock.notifyAll();
}
}
};源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java
private void animate(long frameTimeNs) {
if (!mInitialized) {
return;
}
scheduleAnimation();
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
// 更新窗口动画、prepareSurfaces、提交待处理事务
}动画 callback 在锁内执行并不意味着所有动画计算都能无界耗时;源码中的设计目标是让动画 推进路径明确知道锁归属,并通过 SurfaceAnimationRunner 把不需要持有 WMS 锁的工作继续分流。
9. Surface 分流
源码文件:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
SurfaceAnimationRunner 同时保存 AnimationThread 和 SurfaceAnimationThread 的 Handler。它在 构造时把 Choreographer 获取和 AnimationHandler provider 绑定到 SurfaceAnimationThread, 动画开始、取消和完成则跨两个 Handler 协作。
private final Handler mAnimationThreadHandler = AnimationThread.getHandler();
private final Handler mSurfaceAnimationHandler = SurfaceAnimationThread.getHandler();
private final Choreographer.VsyncCallback mApplyTransactionRunnable =
this::applyTransactionCallback;
private final AnimationHandler mAnimationHandler;动画开始时,Runner 把记录放入 pending 集合,并等待对应的 frame callback:
void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t,
Runnable finishCallback) {
synchronized (mLock) {
final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash,
finishCallback);
mPendingAnimations.put(animationLeash, runningAnim);
if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) {
mChoreographer.postFrameCallback(this::startAnimations);
}
applyTransformation(runningAnim, t, 0 /* currentPlayTime */);
}
}SurfaceAnimationThread 的类注释明确指出它用于运行不持有 WindowManager 锁的 SurfaceAnimationRunner。线程分流的依据是锁和 Surface 操作边界,不是“属性动画”和“位置 动画”这类归档文章的简化分类。
10. 动画取消
源码文件:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
取消路径先在 mLock 下从 pending/pre-processing/running 三类集合中定位动画;运行中的动画 再设置 mCancelled,并把 Animator.cancel() 与事务应用投递给 SurfaceAnimationThread。
void onAnimationCancelled(SurfaceControl leash) {
synchronized (mLock) {
if (mPendingAnimations.containsKey(leash)) {
mPendingAnimations.remove(leash);
return;
}
RunningAnimation anim = mRunningAnimations.get(leash);
if (anim != null) {
mRunningAnimations.remove(leash);
synchronized (mCancelLock) {
anim.mCancelled = true;
}
mSurfaceAnimationHandler.post(() -> {
anim.mAnim.cancel();
applyTransaction(-1);
});
}
}
}取消一个 pending 动画不会发送 Handler 消息;取消一个 running 动画才需要跨线程投递实际 取消和事务应用。状态集合、取消锁和 Handler 各自承担不同责任。
11. 动画完成
源码文件:frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
动画结束时从 running 集合移除;如果没有被取消,完成 callback 被投递到 AnimationThread, 而不是在持有 mLock 时直接调用外部代码。
@Override
public void onAnimationEnd(Animator animation) {
synchronized (mLock) {
mRunningAnimations.remove(a.mLeash);
synchronized (mCancelLock) {
if (!a.mCancelled) {
mAnimationThreadHandler.post(a.mFinishCallback);
}
}
}
}这条路径体现 WMS Handler 文章最重要的边界:锁内只决定状态和投递,外部完成回调在目标 Handler 线程执行。即使 post() 成功,也不能推断 callback 已经完成。
12. 锁外回调
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
NOTIFY_ACTIVITY_DRAWN 在锁内判断 Activity 是否仍 attached,再调用 root task 的通知方法; WAITING_FOR_DRAWN_TIMEOUT 则把保存的 callback 取出后用 sendToTarget()。两者都把“状态 判断”和“外部通知”分成不同阶段,但具体是否完全锁外取决于每个 case 的代码。
case NOTIFY_ACTIVITY_DRAWN: {
final ActivityRecord activity = (ActivityRecord) msg.obj;
synchronized (mGlobalLock) {
if (activity.isAttached()) {
activity.getRootTask().notifyActivityDrawnLocked(activity);
}
}
break;
}不要把所有 H case 归纳为“回调都在锁外”。应逐 case 阅读:如果 case 调用了带 Locked 后缀 的方法,通常说明它仍在全局锁协议内;如果提取 callback 再 sendToTarget,则外部代码可能 在其它 Handler 上运行。
13. 验证输入
源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
第一组验证是等待绘制成功/超时互斥:安排一个 container 和 callback,模拟窗口全部绘制, 断言对应超时消息被移除;另一路不完成窗口,推进 Handler 到 timeout,断言等待集合清空且 callback 被发送一次。关键状态是 mWaitingForDrawnCallbacks 和 mWaitingForDrawn,不是只 看日志。
成功输入:container.mWaitingForDrawn -> empty
成功动作:remove WAITING_FOR_DRAWN_TIMEOUT + send callback
超时输入:timeout Message(container)
超时动作:clear waiting set + remove callback + send callback这证明等待协议,不证明真实 SurfaceFlinger buffer 已显示。
第二组验证是动画去重:调用 WindowAnimator.scheduleAnimation() 两次,断言只向 Choreographer 注册一次 VSYNC callback;执行 callback 后标志清零,下一次调度才可再次注册。
第三组验证是取消分支:对 pending、pre-processing、running 三种状态调用 SurfaceAnimationRunner.onAnimationCancelled(),断言对应集合和 mCancelled 状态变化, 并只对 running 动画验证 SurfaceAnimationThread 收到取消投递。没有完整 WMS 测试环境时, 这些验证应停留在源码/注入测试层,不外推为真实显示提交结果。
14. 复查命令
源码文件:
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.javaframeworks/base/services/core/java/com/android/server/wm/WindowAnimator.javaframeworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.javaframeworks/base/services/core/java/com/android/server/wm/SurfaceAnimationThread.java
rg -n "class H extends|WAITING_FOR_DRAWN_TIMEOUT|NOTIFY_ACTIVITY_DRAWN|handleMessage" \
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
rg -n "waitForAllWindowsDrawn|checkDrawnWindowsLocked|mWaitingForDrawnCallbacks|sendNewMessageDelayed" \
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
rg -n "mAnimationHandler|scheduleAnimation|mAnimationVsyncCallback|animate\(" \
frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java
rg -n "mAnimationThreadHandler|mSurfaceAnimationHandler|startAnimation|onAnimationCancelled|applyTransaction" \
frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java阅读一条 WMS 调度路径时,至少标出四个位置:消息进入哪个 Looper、哪个锁拥有状态、哪个 对象负责重新检查 timeout/animation 状态、哪个 Handler 执行外部 callback。只看到 mH 或 post(),无法判断窗口绘制和动画是否已经完成。
15. 使用边界
WMS Handler 的核心作用是把窗口状态检查、超时、绘制通知和动画预约放入可定位的 Looper/锁 协议中。它不提供窗口绘制完成的强实时保证,不替代 SurfaceFlinger 提交,也不让动画 callback 自动脱离全局锁。
排查“窗口一直等不到绘制”时,检查等待集合、超时 Message 是否仍存在、成功绘制是否移除了 超时、callback target 是否仍有效;排查“动画卡顿”时,继续追踪 AnimationThread、 SurfaceAnimationThread、Choreographer frame callback、取消锁和事务应用,而不是只检查 WMS.H 是否收到消息。
