Skip to content

WMS Handler 分流

从 WMS.H 的窗口绘制超时和动画预约路径,追踪 mGlobalLock、等待回调、AnimationThread 与 SurfaceAnimationThread 的职责边界。

基于android-17.0.0_r1
AndroidWindowManagerServiceWindowAnimatorHandler源码阅读

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 线程;动画协作者可以绑定其它专用线程。

java
final H mH = new H();
final WindowAnimator mAnimator;
SurfaceAnimationRunner mSurfaceAnimationRunner;

H 的消息 case 大多在 mGlobalLock 下读取或更新窗口状态,但并不是每个动作都在锁内完成。 例如等待绘制超时会在锁内提取 callback,再在锁外发送;动画则通过独立线程减少持有 WMS 锁的 时间。

对象Looper/线程主要状态典型消费者
WMS.HWMS 主 Looper窗口、焦点、超时、显示状态WindowManagerService/DisplayContent
WindowAnimatorAnimationThread + Choreographer窗口动画帧animate()
SurfaceAnimationRunnerAnimationThread/SurfaceAnimationThreadSurfaceControl 动画applyTransaction()
mGlobalLock共享锁WMS 全局状态一致性上述多个消费者

2. 消息入口 ​

源码文件:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

WMS.H 是普通 Handler 子类,整数 what 表示协议,msg.obj 载荷保存具体 WindowContainer、 ActivityRecord 或回调 Message。本文只列与主线直接相关的几类:

java
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 持有。

java
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。

java
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。

java
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 已经完成所有显示提交。

java
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 等消息。 它们共享“消息触发后重新检查状态”的模式,而不是把延迟时间本身当成事实。

java
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 中推进每 个动画属性。

java
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 状态。

java
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

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 协作。

java
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:

java
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。

java
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 时直接调用外部代码。

java
@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 的代码。

java
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,不是只 看日志。

text
成功输入: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.java
  • frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java
  • frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
  • frameworks/base/services/core/java/com/android/server/wm/SurfaceAnimationThread.java
bash
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 是否收到消息。