ANR 消息边界
ANR 不是“主线程超过某个固定毫秒数”这一条规则,而是某个受监管的事件或服务没有在其 当前 deadline 前完成。Android 17 至少有三层相关实现:InputDispatcher 追踪 InputChannel 等待 ACK;AMS/ActiveServices 为 Service 执行计时;AnrHelper 在检测结果产生后异步收集 trace 和处理记录。主线程 MessageQueue 阻塞会让应用无法消费输入或 Service 生命周期消息, 但 ANR 的检测者、deadline 和后续处理并不都在这个 MessageQueue 中。
本文面向已经读过 Looper消息监控、输入事件派发路径、 AMS Handler 分流 和 WMS Handler 分流 的读者。本文只追踪输入 dispatch timeout、Service timeout 和 AnrHelper 的连接,不列出所有 ANR 类型、固定秒数或产品策略。重点是辨认“谁启动 deadline、谁发现过期、谁提交处理、哪条 消息仍可能在主线程排队”。
读完后,读者应能从 ANR reason 反查检测 owner,区分 InputDispatcher 的 native wait queue 与应用 MessageQueue,解释 Service timeout 消息为何需要在成功路径撤销,并判断 AnrHelper 开始处理后为什么不等于用户对话框已经显示。
1. 检测分层
源码文件:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
InputDispatcher 在 native 线程中维护 connection 的 wait queue 和 mAnrTracker;它根据每个 window 的 dispatching timeout 计算下一次 ANR 检查时间。
nsecs_t InputDispatcher::processAnrsLocked() {
const nsecs_t currentTime = now();
nsecs_t nextAnrCheck = LLONG_MAX;
if (mNoFocusedWindowAnrState.has_value()) {
if (currentTime >= mNoFocusedWindowAnrState->timeoutEndTime) {
processNoFocusedWindowAnrLocked();
resetNoFocusedWindowTimeoutLocked();
return LLONG_MIN;
}
nextAnrCheck = mNoFocusedWindowAnrState->timeoutEndTime;
}
nextAnrCheck = std::min(nextAnrCheck, mAnrTracker.firstTimeout());
if (currentTime < nextAnrCheck) {
return nextAnrCheck;
}
// 找到过期 connection 并调用 onAnrLocked
}InputDispatcher 的检查并不依赖应用主线程 Handler;它自己的 Looper/线程在 deadline 到达时 发现 ACK 等待超时,再通过 policy 进入 system_server 的 ANR 处理。
2. 输入 deadline
源码文件:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
事件写入 connection 后,startDispatchCycleLocked() 为 dispatch entry 写入 delivery time 和 timeout time。timeout 可由窗口配置提供,否则使用默认值;不能把一个旧版本的固定常数 写成所有窗口的当前保证。
const std::chrono::nanoseconds timeout = getDispatchingTimeoutLocked(connection);
dispatchEntry->deliveryTime = currentTime;
dispatchEntry->timeoutTime = currentTime + timeout.count();超时值由 connection/window 状态继续决定:
std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked(
const std::shared_ptr<Connection>& connection) {
if (connection->isFocusMonitor) {
return mMonitorDispatchingTimeout;
}
const sp<WindowInfoHandle> window =
mWindowInfos.findWindowHandle(connection->getToken());
if (window != nullptr) {
return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
}
return DEFAULT_INPUT_DISPATCHING_TIMEOUT;
}应用收到事件后,必须沿 InputEventReceiver → ViewRootImpl → finishInputEvent() 回写 ACK; 消息异步只改变 MessageQueue 的选择资格,不会让 native wait queue 认为事件已完成。
3. 输入过期
源码文件:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
当 mAnrTracker.firstTimeout() 到期,InputDispatcher 找到 connection,标记为不响应,移除 tracker token,并调用 onAnrLocked()。如果 connection 已不存在,tracker entry 会被删除, 避免同一错误反复记录。
std::shared_ptr<Connection> connection =
mConnectionManager.getConnection(mAnrTracker.firstToken());
if (connection == nullptr) {
mAnrTracker.eraseToken(mAnrTracker.firstToken());
return nextAnrCheck;
}
connection->responsive = false;
mAnrTracker.eraseToken(connection->getToken());
onAnrLocked(connection);
return LLONG_MIN;onAnrLocked() 还会再次检查 wait queue 是否已经恢复为空;如果应用恰好在检测前完成 ACK, 源码会记录“已恢复”并不升格为 ANR。这是检测时状态复查,而不是 Handler 的取消消息。
if (connection->waitQueue.empty()) {
ALOGI("Not raising ANR because the connection has recovered");
return;
}4. 服务计时器
源码文件:frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
Service 超时由 ActiveServices 的计时器对象维护,计时消息使用 AMS mHandler 的消息编号 消费。当前源码把普通执行服务、短时前台服务和启动前台服务拆成不同 timer,不应简化成一个 固定 SERVICE_TIMEOUT。
private final ProcessAnrTimer mActiveServiceAnrTimer =
new ProcessAnrTimer(service,
ActivityManagerService.SERVICE_TIMEOUT_MSG,
"SERVICE_TIMEOUT", /* args */);
private final ServiceAnrTimer mServiceFGAnrTimer =
new ServiceAnrTimer(service,
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG,
"SERVICE_FOREGROUND_TIMEOUT", /* args */);服务开始执行时,timer 记录 ProcessRecord/ServiceRecord 和 deadline;服务完成、停止或状态 改变时由对应 owner 清理 timer。消息到达后,ActiveServices 还要检查进程是否仍执行服务、 是否被杀和当前 foreground 状态。
5. 服务超时消费
源码文件:frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
普通 service timeout 的消费方法先在 AMS 锁下确认进程和执行服务状态,再选择实际超时记录, 最后交给 AnrHelper 或 warning 路径。消息进入 AMS Handler 不等于一定会产生 ANR。
void serviceTimeout(ProcessRecord proc) {
try {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout()");
synchronized (mAm) {
if (proc.isDebugging() || proc.mServices.numberOfExecutingServices() == 0
|| proc.getThread() == null || proc.isKilled()) {
return;
}
// 找到仍然超时的 ServiceRecord,再进入 ANR 处理
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}这条路径和输入 ANR 的检测者不同:InputDispatcher 在 native connection 上计时,Service timeout 由 system_server 的 AMS Handler/计时器消费,但最终都需要判断目标状态是否仍成立。
6. ANR 记录
源码文件:frameworks/base/services/core/java/com/android/server/am/AnrHelper.java
AnrHelper.appNotResponding() 不同步执行完整 dump,而是先在 mAnrRecords 锁下去重、提交 早期进程 dump,并把 AnrRecord 放入队列。
void appNotResponding(ProcessRecord anrProcess, TimeoutRecord timeoutRecord) {
final int incomingPid = anrProcess.mPid;
synchronized (mAnrRecords) {
if (incomingPid == 0 || mProcessingPid == incomingPid) {
return;
}
if (!mTempDumpedPids.add(incomingPid)) {
return;
}
Future<File> firstPidDumpPromise = mEarlyDumpExecutor.submit(() ->
StackTracesDumpHelper.dumpStackTracesTempFile(
incomingPid, timeoutRecord.mLatencyTracker));
mAnrRecords.add(new AnrRecord(
anrProcess, timeoutRecord, firstPidDumpPromise));
}
startAnrConsumerIfNeeded();
}去重条件意味着同一 pid 的 ANR 可能被跳过;“检测到超时”与“新增一条 ANR 处理记录”不是 一一对应。
7. ANR 消费线程
源码文件:frameworks/base/services/core/java/com/android/server/am/AnrHelper.java
startAnrConsumerIfNeeded() 使用原子 mRunning 保证只启动一个 consumer。consumer 从记录 队列取出一个 ANR,检查进程 pid 是否仍匹配,再调用 AnrRecord.appNotResponding()。
private void startAnrConsumerIfNeeded() {
if (mRunning.compareAndSet(false, true)) {
new AnrConsumerThread().start();
}
}
private AnrRecord next() {
synchronized (mAnrRecords) {
if (mAnrRecords.isEmpty()) return null;
AnrRecord record = mAnrRecords.remove(0);
mProcessingPid = record.mPid;
return record;
}
}取出记录后,consumer 才检查 pid 是否仍匹配并执行 ANR 处理:
public void run() {
AnrRecord record;
while ((record = next()) != null) {
final int currentPid = record.mApp.mPid;
if (currentPid != record.mPid) {
continue;
}
record.appNotResponding(/* onlyDumpSelf */ false);
}
mRunning.set(false);
}ANR consumer 不是 AMS MainHandler,也不是 InputDispatcher 线程;它是后续处理线程。主线程 阻塞可能是 ANR 诱因,但 ANR trace 采集和处理不应再压回同一条主 Handler。
8. 消息阻塞
源码文件:frameworks/base/core/java/android/os/Looper.java
Looper 的 Printer/slow log 可以区分 delivery 与 dispatch,但 ANR 检测不只是“某条消息慢”。 如果主线程在长 callback 内,下一条输入消息可能已经被 MessageQueue 接收但尚未 dispatch; InputDispatcher 观察的是 ACK wait queue,Looper 观察的是自身 dispatch 区间。
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
msg.target.dispatchMessage(msg);
final long dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;主线程也可能阻塞在锁、Binder、native I/O 或 GC,而不是 Java Handler 的 handleMessage() 代码;因此 Message logging 只能缩小范围,不能替代 ANR trace。
9. 输入与 Handler
源码文件:frameworks/base/core/java/android/view/ViewRootImpl.java
ViewRootImpl 把输入事件放进自己的 pending queue,必要时发送异步 MSG_PROCESS_INPUT_EVENTS; 即使该消息绕过 traversal barrier,也不能抢占当前正在执行的 callback。
private void scheduleProcessInputEvents() {
if (!mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = true;
Message msg = mHandler.obtainMessage(MSG_PROCESS_INPUT_EVENTS);
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
}这条代码解释“输入消息在队列里但仍发生 ANR”:异步只改变屏障选择,当前主线程忙碌时仍不能 取出并完成 finishInputEvent()。
10. 超时复查
源码文件:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
InputDispatcher 在 onAnrLocked() 中复查 wait queue;WMS/AMS 的 Service timeout 也会在 handler case 中检查进程、线程和执行服务状态。所有检测器都遵循“deadline 到达后再次确认” 原则,避免把已恢复对象误报为 ANR。
deadline 到达
-> 查找仍存在的 connection/ProcessRecord/ServiceRecord
-> 检查 wait queue、进程存活和执行状态
-> 仍未完成:标记 unresponsive,进入 ANR 处理
-> 已恢复/对象消失:清理计时状态,不升级因此不能只看一条“timeout message”就断言 ANR 已发生;应跟进检测函数的最终条件和状态写入。
11. Watchdog 边界
源码文件:frameworks/base/services/core/java/com/android/server/Watchdog.java
Watchdog 监控的是 system_server Handler 线程,通过把检查 Runnable 投递到目标 Handler,再 观察 mCompleted 是否在 deadline 前变为 true。它保护的是 system_server 线程活性,不是应用 输入/Service ANR。
public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) {
mCompleted = false;
mStartTime = SystemClock.uptimeMillis();
mHandler.postAtFrontOfQueue(this);
}
@Override
public void run() {
mCompleted = true;
}把 Watchdog 的 system_server 处置逻辑和应用 ANR 混为一谈,会错误地认为每个 Handler timeout 都会杀进程;两者检测 owner、结果和恢复策略不同。
12. 证据阅读
源码文件:frameworks/base/services/core/java/com/android/server/am/AnrHelper.java
AnrHelper 记录 mTimestamp,consumer 计算 report latency;如果处理排队太久,源码会选择 只 dump ANR 进程,避免收集已经过时且进一步增加系统负载的全量信息。
final long startTime = SystemClock.uptimeMillis();
final long reportLatency = startTime - record.mTimestamp;
final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS
|| startTime < SELF_ONLY_AFTER_BOOT_MS;
record.appNotResponding(onlyDumpSelf);这说明 traces 的内容受采集时延和启动阶段影响;一份 trace 是某个时间点的证据,不是永远 有效的线程状态快照。
13. 验证输入
源码文件:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
第一组验证设置 connection 的 dispatch timeout,发送事件但不回 ACK,推进 dispatcher 时钟, 断言 processAnrsLocked() 标记 connection 不响应;随后补 ACK/清空 wait queue,验证恢复 分支不会再次升级。
第二组验证使用 AMS/ActiveServices 的注入 Handler:安排 Service timeout,先模拟服务完成并 移除 timer,再推进 Handler;断言不会调用 ANR 处理。另一输入保持 executing service 状态, 断言进入 timeout/AnrHelper。
第三组验证 AnrHelper:提交同一 pid 的两个 appNotResponding(),断言处理中/已排队/预 dump 状态会去重;让 consumer 延迟,验证 onlyDumpSelf 的过期分支。以上验证不能外推固定秒数或 真实设备 CPU/IO 行为。
14. 复查命令
源码文件:
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cppframeworks/base/services/core/java/com/android/server/am/ActiveServices.javaframeworks/base/services/core/java/com/android/server/am/AnrHelper.javaframeworks/base/core/java/android/os/Looper.javaframeworks/base/core/java/android/view/ViewRootImpl.javaframeworks/base/services/core/java/com/android/server/Watchdog.java
rg -n "processAnrsLocked|mAnrTracker|getDispatchingTimeoutLocked|onAnrLocked" \
frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
rg -n "ProcessAnrTimer|SERVICE_TIMEOUT_MSG|serviceTimeout|scheduleServiceTimeout" \
frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
rg -n "appNotResponding|mAnrRecords|startAnrConsumerIfNeeded|onlyDumpSelf" \
frameworks/base/services/core/java/com/android/server/am/AnrHelper.java
rg -n "MSG_PROCESS_INPUT_EVENTS|setAsynchronous|doProcessInputEvents" \
frameworks/base/core/java/android/view/ViewRootImpl.java阅读 ANR 时应按“检测 owner → deadline 状态 → 最终复查 → 处理 owner → 证据时间点”走源码。 只看到主线程 stack 在 MessageQueue 或某个 Handler 中,不能单独证明 ANR 类型和根因。
15. 适用边界
消息队列阻塞是许多 ANR 的共同诱因,但不是所有 ANR 的统一实现。InputDispatcher、AMS Service timer、Watchdog 和 AnrHelper 各有独立 owner、线程和状态。异步 Message 不能抢占忙碌 主线程,Handler 日志不能替代 native dispatcher 状态和 ANR trace。
排查时先确认 ANR reason,再回到对应检测器:输入看 connection wait queue 与 ACK,Service 看执行记录和 timer,system_server 看 Watchdog,处理阶段看 AnrHelper 的去重、排队和采集时延。 这样才能避免用一个“主线程卡住”结论覆盖不同的源码路径。
