Skip to content

AMS Handler 分流

从 ActivityManagerService 的 ServiceThread、MainHandler、UiHandler 和进程启动超时路径,解释 system_server 内的消息分流与清理。

基于android-17.0.0_r1
AndroidActivityManagerServiceMainHandlerUiHandler源码阅读

AMS Handler 分流 ​

ActivityManagerService 并不是“一个 Handler 处理所有事情”。Android 17 的 AMS 至少把消息 分到主调度线程、system_server UI 线程和进程启动专用线程;不同 Handler 的差异首先是 Looper 所有权,其次才是消息类型。本文选择一条可完整追踪的进程启动超时路径,并用 UI 错误对话框和 ANR 辅助处理作对照,解释 Binder 调用、锁内状态、延迟消息、取消和失败清理如何分开。

本文面向已经读过 ActivityThread.H 主线程、 HandlerThread 生命周期、消息延迟边界 和 HandlerExecutor 提交语义 的读者。本文不 罗列 AMS 的全部 what 常量,也不把 system_server 的所有 ServiceThread 都当成 AMS owner; 只讨论 ActivityManagerService 文件中实际创建和消费的 Handler,以及 AnrHelper 的独立 执行器边界。

读完后,读者应能判断某个 AMS 消息属于哪个 Looper,解释进程启动超时为什么需要投递和撤销 两条消息,区分 UiHandler 显示错误 UI 与 MainHandler 修改核心状态的线程边界,并定位进程 死亡时哪些延迟消息会被移除。

1. 线程所有权 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

AMS 的正式构造函数创建一个前台优先级 ServiceThread 作为主 Handler 线程,再创建 MainHandler;同时把 UI Handler 交给 injector,并为进程启动路径保留另一条线程和 ProcStartHandler。

java
mHandlerThread = new ServiceThread(TAG,
        THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
mHandlerThread.start();
mHandler = new MainHandler(mHandlerThread.getLooper());
mUiHandler = mInjector.getUiHandler(this);

mProcStartHandlerThread = new ServiceThread(TAG + ":procStart",
        THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
mProcStartHandlerThread.start();
mProcStartHandler = new ProcStartHandler(this, mProcStartHandlerThread.getLooper());

测试构造器则允许注入已有 ServiceThread,把同一个 Looper 交给 MainHandler,方便隔离 AMS 状态逻辑。Handler 的线程来源因此是构造时传入的 Looper,不是消息发送方的 Binder 线程。

HandlerLooper owner典型消费者是否主 AMS 状态线程
mHandlermHandlerThread超时、GC、进程/服务状态是
mUiHandlerUiThread 或 injector 提供错误/ANR/调试 UI否
mProcStartHandlermProcStartHandlerThread进程启动专用步骤否

2. MainHandler 构造 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

MainHandler 通过隐藏构造器创建,async=true 让它发送的消息标记异步;这只改变消息在同步 屏障下的选择资格,不改变 Handler 绑定的 ServiceThread。

java
final class MainHandler extends Handler {
    public MainHandler(Looper looper) {
        super(looper, null, true);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case GC_BACKGROUND_PROCESSES_MSG:
                synchronized (mGlobalLock) {
                    mAppProfiler.performAppGcsIfAppropriateLocked();
                }
                break;
            case SERVICE_TIMEOUT_MSG:
                mServices.serviceTimeout((ProcessRecord) msg.obj);
                break;
            case PROC_START_TIMEOUT_MSG:
                synchronized (mGlobalLock) {
                    handleProcessStartOrKillTimeoutLocked(
                            (ProcessRecord) msg.obj, false /* isKillTimeout */);
                }
                break;
        }
    }
}

handleMessage() 中的锁不是 Handler 自动提供的同步机制。每个 case 自己决定是否需要 mGlobalLock、mProcLock 或其它 owner 锁;消息进入主线程只保证执行线程,不自动保证 AMS 状态访问安全。

3. UiHandler 构造 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

UiHandler 显式绑定 com.android.server.UiThread 的 Looper,并同样使用异步 Handler。它负责 把核心调度线程不应直接执行的错误/ANR UI 交给 system_server 的 UI 线程。

java
final class UiHandler extends Handler {
    public UiHandler() {
        super(com.android.server.UiThread.get().getLooper(), null, true);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case SHOW_ERROR_UI_MSG:
                mAppErrors.handleShowAppErrorUi(msg);
                ensureBootCompleted();
                break;
            case SHOW_NOT_RESPONDING_UI_MSG:
                mAppErrors.handleShowAnrUi(msg);
                ensureBootCompleted();
                break;
        }
    }
}

“UiHandler 不阻塞 MainHandler”不是绝对的性能保证:两个线程仍可能通过共享 AMS 状态、 Window/UI 服务或锁互相等待。源码能够保证的是消息执行线程不同,不能保证所有下游调用都 无锁或无 I/O。

4. 消息路由 ​

AMS 的 Binder 方法通常先完成权限和状态检查,再根据消费者选择 Handler。核心状态修改 必须遵守对应锁;UI 提示和耗时辅助工作则可以转移到其他 owner。

图中 Binder 线程只是入口,不是所有操作的执行者。真正的状态 owner 由 Handler 消费方法和 锁共同决定;不要只看到 sendMessage() 就断言“调用已异步完成”。

5. 启动超时 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

当 AMS 向新进程发送 bind application 后,会在 mHandler 上安排软超时消息,并在成功 attach 时移除相关超时。消息的 obj 是 ProcessRecord,所以超时消费者可以重新定位具体进程。

java
Message msg = mHandler.obtainMessage(BIND_APPLICATION_TIMEOUT_SOFT_MSG);
msg.obj = app;
msg.arg1 = BIND_APPLICATION_TIMEOUT;
mHandler.sendMessageDelayed(msg, msg.arg1);

成功 attach 的路径移除软、硬两类超时:

java
mHandler.removeMessages(BIND_APPLICATION_TIMEOUT_SOFT_MSG, app);
mHandler.removeMessages(BIND_APPLICATION_TIMEOUT_HARD_MSG, app);

这里的取消匹配同时使用 what 和 app,避免一个进程的成功 attach 把另一个 ProcessRecord 的超时消息误删。消息从队列移除只是取消检查,不等于撤销进程已经执行的 工作。

6. 超时升级 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

软超时处理不是直接杀进程,而是根据状态决定是否延长并安排硬超时;硬超时最终把问题交给 AnrHelper.appNotResponding()。

java
private void handleBindApplicationTimeoutSoft(ProcessRecord app) {
    final long hardTimeoutMillis = getBindApplicationTimeoutMillis(app);
    if (hardTimeoutMillis <= 0) {
        handleBindApplicationTimeoutHard(app);
        return;
    }

    Message msg = mHandler.obtainMessage(BIND_APPLICATION_TIMEOUT_HARD_MSG, app);
    mHandler.sendMessageDelayed(msg, hardTimeoutMillis);
}

private void handleBindApplicationTimeoutHard(ProcessRecord app) {
    final String anrMessage = "Process " + app + " failed to complete startup";
    mAnrHelper.appNotResponding(app, TimeoutRecord.forAppStart(anrMessage));
}

软/硬超时消息的生效点都在 MainHandler 所属线程;AnrHelper 接管后,栈转储和 ANR 记录 处理不再由这个 Handler 同步完成。

7. ANR 辅助 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/AnrHelper.java

AnrHelper 的类注释明确说明,它按需创建独立线程,避免调用方因为 ANR 处理耗时而等待。 Android 17 实现使用 expiring ThreadPoolExecutor,而不是把所有工作 post 到 AMS MainHandler。

java
private final ExecutorService mAuxiliaryTaskExecutor;
private final ExecutorService mEarlyDumpExecutor;
private final AtomicBoolean mRunning = new AtomicBoolean(false);

AnrHelper(ActivityManagerService service) {
    this(service,
            makeExpiringThreadPoolWithSize(1, sDefaultThreadFactory),
            makeExpiringThreadPoolWithSize(2, sMainProcessDumpThreadFactory));
}

收到 ANR 后先在 mAnrRecords 下去重、记录并提交早期 dump,再启动 consumer thread。这里 的异步边界是 Executor,不是 AMS Handler;如果把 ANR 处理误写成 mHandler.post(),会丢失 实际线程和队列模型。

8. UI 错误 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

UI Handler 的消息 payload 通常包含 ProcessRecord、错误结果或对话框状态。以 StrictMode UI 消息为例,处理方法在 mProcLock 下重新检查进程是否存在和是否已有对话框,再决定显示或直接 结束结果。

java
case SHOW_STRICT_MODE_VIOLATION_UI_MSG: {
    HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
    synchronized (mProcLock) {
        ProcessRecord proc = (ProcessRecord) data.get("app");
        if (proc == null) {
            Slog.e(TAG, "App not found when showing strict mode dialog.");
            break;
        }
        if (proc.mErrorState.getDialogController().hasViolationDialogs()) {
            return;
        }
        AppErrorResult result = (AppErrorResult) data.get("result");
        if (mAtmInternal.showStrictModeViolationDialog()) {
            proc.mErrorState.getDialogController().showViolationDialogs(result);
        } else {
            result.set(0);
        }
    }
    ensureBootCompleted();
    break;
}

消息到达 UI 线程后仍要重新检查状态,因为 Binder 入口到 UI 消费之间可能已经发生进程死亡、 策略变化或对话框重复注册。Handler 只提供排队,不冻结 payload 指向的业务对象。

9. 周期消息 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

MainHandler 也用“移除旧消息再安排下一次”的方式做周期检查,例如 excessive power use。 这不是定时器线程,而是一个在 Handler Looper 上自我重排的消息协议。

java
case CHECK_EXCESSIVE_POWER_USE_MSG: {
    checkExcessivePowerUsage();
    removeMessages(CHECK_EXCESSIVE_POWER_USE_MSG);
    Message next = obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
    sendMessageDelayed(next, mConstants.POWER_CHECK_INTERVAL);
    break;
}

如果 Looper 退出或消息被清理,周期链条就会停止;如果处理本身耗时,下一次安排发生在当前 case 之后。源码没有提供固定实时周期保证。

10. 进程死亡 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

进程死亡会清理与该 ProcessRecord 相关的启动、绑定和 provider 超时消息,防止旧消息在进程 已经消失后继续操作过期状态。

java
mHandler.removeMessages(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG, app);
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
mHandler.removeMessages(BIND_APPLICATION_TIMEOUT_SOFT_MSG, app);
mHandler.removeMessages(BIND_APPLICATION_TIMEOUT_HARD_MSG, app);

这些移除动作通常发生在 AMS 已经持有相应进程锁的生命周期路径中。只删除消息不等于删除 ProcessRecord 的所有引用;Activity、Service、BatteryStats 和观察者仍由各自 owner 清理。

11. 锁与回调 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

MainHandler 的 case 有的在 mGlobalLock 内调用状态方法,有的直接调用 ActiveServices; UiHandler 也会拿 mProcLock。消息切线程不能替代锁设计,反而要求读者同时看“消息线程”和 “状态锁”。

java
case GC_BACKGROUND_PROCESSES_MSG:
    synchronized (mGlobalLock) {
        mAppProfiler.performAppGcsIfAppropriateLocked();
    }
    break;
case SERVICE_TIMEOUT_MSG:
    mServices.serviceTimeout((ProcessRecord) msg.obj);
    break;

如果某个 handler callback 再同步等待另一个持锁线程,就可能形成跨线程等待;本文不能从 “使用 Handler”推出没有死锁或没有长耗时。

12. 测试入口 ​

源码文件:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

AMS 提供测试构造器注入 ServiceThread 和 Injector,使测试能够控制 MainHandler 的 Looper 和依赖对象。这个设计本身就是一个可读源码入口:测试不必启动完整 system_server,也能验证 消息处理方法对状态 owner 的影响。

java
@VisibleForTesting
ActivityManagerService(Injector injector, @NonNull ServiceThread handlerThread) {
    this(injector, handlerThread, null);
}

@VisibleForTesting
ActivityManagerService(Injector injector, @NonNull ServiceThread handlerThread,
        @Nullable UserController userController) {
    mHandler = new MainHandler(handlerThread.getLooper());
    mHandlerThread = handlerThread;
    mConstants = new ActivityManagerConstants(mContext, this, mHandler);
}

可执行验证应安排一个超时消息,推进注入 Looper,再检查对应状态方法;同时安排成功 attach 输入,确认 removeMessages(what, app) 后超时不会再次处理。它证明消息和 owner 状态关系, 不证明真实 Binder 时序或所有系统服务并发关系。

13. 复查命令 ​

源码文件:

  • frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
  • frameworks/base/services/core/java/com/android/server/am/AnrHelper.java
  • frameworks/base/services/core/java/com/android/server/UiThread.java
bash
rg -n "mHandlerThread|mHandler = new MainHandler|mUiHandler|mProcStartHandler" \
  frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

rg -n "class MainHandler|class UiHandler|handleMessage|SERVICE_TIMEOUT_MSG|BIND_APPLICATION_TIMEOUT" \
  frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

rg -n "sendMessageDelayed|removeMessages|appNotResponding|startAnrConsumerIfNeeded" \
  frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java \
  frameworks/base/services/core/java/com/android/server/am/AnrHelper.java

阅读 AMS Handler 时应按“消息常量 → payload → Handler Looper → 锁/状态 owner → 取消/恢复” 顺序走源码。只列 MainHandler 和 UiHandler 的名字,无法解释一条超时消息最终改变了哪个 ProcessRecord 或为什么成功 attach 后不会误触发 ANR。

14. 使用边界 ​

AMS 的 Handler 分流解决的是线程归属和消息生命周期,不是自动性能优化。MainHandler 适合 短小、需要 AMS 核心状态锁的调度;UiHandler 适合 UI 消费;ANR dump 等长任务使用独立 Executor。每条消息都仍可能延迟、被移除、在消费时发现 payload 已失效,或因 Looper 退出而 不再执行。

排查 AMS 超时问题时,应检查:消息是否发到正确 Handler,payload 是否仍对应当前 ProcessRecord,成功路径是否移除了旧消息,Handler callback 是否持锁调用耗时逻辑,以及 ANR/IO 工作是否已转移到独立执行器。这样才能区分调度延迟、状态竞态和真正的服务超时。