ANR监控原理

3 阅读15分钟

ANR监控原理

面试重要度:⭐⭐⭐⭐⭐

考察频率:字节 85% | 阿里 75% | 腾讯 70%

一、核心概念

1.1 定义与作用

一句话定义: ANR(Application Not Responding)监控是通过 Handler 消息机制实现的应用响应性检测系统,系统在关键操作前埋入超时消息,若操作未在规定时间内完成则触发 ANR 弹窗。

为什么重要

  • ANR 是 Android 系统保护用户体验的核心机制,直接影响应用稳定性评分
  • 字节跳动等大厂将 ANR 率作为核心性能指标,P5/P6 必须掌握监控原理
  • 理解 ANR 原理是进行性能优化和线上问题排查的基础
  • 体现候选人对 Handler 机制的深入理解程度

1.2 ANR 触发场景与超时时间

场景超时时间触发条件
输入事件分发5秒KeyEvent/TouchEvent 未在时限内处理完成
Service 前台20秒startForegroundService() 后未及时调用 startForeground()
Service 后台200秒Service 生命周期方法执行超时
BroadcastReceiver 前台10秒前台广播 onReceive() 执行超时
BroadcastReceiver 后台60秒后台广播 onReceive() 执行超时
ContentProvider10秒ContentProvider 发布超时

1.3 与 Handler 机制的关系

ANR 监控本质是 Handler 延迟消息的典型应用:

  • 埋炸弹:在操作开始前发送一个延迟消息到主线程 MessageQueue
  • 拆炸弹:操作完成后移除该延迟消息
  • 引爆炸弹:若延迟消息未被移除并被执行,触发 ANR

这与 03-主线程Looper创建时机.md 中讲解的主线程消息循环密切相关。


二、核心原理

2.1 ANR 监控整体架构

┌─────────────────────────────────────────────────────────────┐
│                      System Server                           │
├──────────────┬──────────────┬──────────────┬────────────────┤
│     AMS      │     WMS      │     IMS      │  BroadcastQueue│
│ (Service ANR)│ (Input ANR) │ (Input ANR)  │ (Broadcast ANR)│
└──────┬───────┴──────┬───────┴──────┬───────┴───────┬────────┘
       │              │              │               │
       ▼              ▼              ▼               ▼
┌─────────────────────────────────────────────────────────────┐
│              Handler + MessageQueue (延迟消息机制)            │
│                                                             │
│   scheduleXxxTimeoutLocked() ─→ handleXxxTimeout()          │
│         (埋炸弹)                    (引爆)                   │
└─────────────────────────────────────────────────────────────┘

2.2 Service ANR 监控源码分析

核心流程:启动 Service → 埋超时消息 → 执行生命周期 → 移除超时消息

// Android 11 源码:frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

// 步骤1:启动 Service 时埋炸弹
private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
        boolean execInFg) throws RemoteException {
    // 调度超时检测(埋炸弹)
    bumpServiceExecutingLocked(r, execInFg, "create");

    // 通过 Binder 通知应用进程创建 Service
    app.thread.scheduleCreateService(r, r.serviceInfo, ...);
}

// 埋炸弹的具体实现
private void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
    // 计算超时时间:前台20秒,后台200秒
    long now = SystemClock.uptimeMillis();
    if (r.executeNesting == 0) {
        r.executeFg = fg;
        // 调度超时消息
        scheduleServiceTimeoutLocked(r.app);
    }
    r.executeFg |= fg;
    r.executeNesting++;
    r.executingStart = now;
}

// 发送延迟消息
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.executingServices.size() == 0 || proc.thread == null) {
        return;
    }
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    // 前台服务20秒,后台服务200秒
    mAm.mHandler.sendMessageDelayed(msg,
            proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

源码解读

  • bumpServiceExecutingLocked() 在 Service 启动时调用,开始计时
  • scheduleServiceTimeoutLocked() 发送延迟消息到 AMS 的 Handler
  • 前台服务超时 SERVICE_TIMEOUT = 20 * 1000(20秒)
  • 后台服务超时 SERVICE_BACKGROUND_TIMEOUT = 200 * 1000(200秒)

拆炸弹流程

// Android 11 源码:frameworks/base/services/core/java/com/android/server/am/ActiveServices.java

// Service 生命周期执行完成后调用
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
        boolean finishing) {
    r.executeNesting--;
    if (r.executeNesting <= 0) {
        if (r.app != null) {
            // 从正在执行的服务列表移除
            r.app.execServicesFg = false;
            r.app.executingServices.remove(r);
            if (r.app.executingServices.size() == 0) {
                // 关键:移除超时消息(拆炸弹)
                mAm.mHandler.removeMessages(
                        ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
            }
        }
    }
}

引爆炸弹流程

// Android 11 源码:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

final class MainHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case SERVICE_TIMEOUT_MSG: {
                // 超时消息未被移除,触发 ANR
                mServices.serviceTimeout((ProcessRecord) msg.obj);
            } break;
        }
    }
}

// ActiveServices.java
void serviceTimeout(ProcessRecord proc) {
    // ... 检查是否真的超时
    if (anrMessage != null) {
        // 触发 ANR 弹窗
        mAm.mAnrHelper.appNotResponding(proc, anrMessage);
    }
}

2.3 Broadcast ANR 监控源码分析

广播超时检测流程

// Android 11 源码:frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java

// 步骤1:发送广播时埋炸弹
final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
    BroadcastRecord r;
    // ...

    // 有序广播处理
    if (r == null) {
        r = mDispatcher.getNextBroadcastLocked(now);
    }

    if (r != null) {
        // 设置超时时间并发送延迟消息
        long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
        setBroadcastTimeoutLocked(timeoutTime);
        // ...
    }
}

// 埋炸弹实现
final void setBroadcastTimeoutLocked(long timeoutTime) {
    if (!mPendingBroadcastTimeoutMessage) {
        Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
        mHandler.sendMessageAtTime(msg, timeoutTime);
        mPendingBroadcastTimeoutMessage = true;
    }
}

超时时间常量

// Android 11 源码:frameworks/base/services/core/java/com/android/server/am/BroadcastConstants.java

// 前台广播超时:10秒
public int TIMEOUT = 10 * 1000;

// 后台广播超时:60秒(在 BroadcastQueue 构造时根据队列类型设置)

拆炸弹

// 广播接收完成后调用
final void cancelBroadcastTimeoutLocked() {
    if (mPendingBroadcastTimeoutMessage) {
        mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
        mPendingBroadcastTimeoutMessage = false;
    }
}

2.4 Input ANR 监控源码分析

Input ANR 与 Service/Broadcast ANR 机制不同,采用的是事件分发超时检测:

// Android 11 源码:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

// 检查窗口是否响应超时
std::optional<nsecs_t> InputDispatcher::getAnrTimeoutLocked(
        const sp<InputApplicationHandle>& applicationHandle,
        const sp<InputWindowHandle>& windowHandle) {
    // 默认超时时间 5秒
    constexpr nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL;

    nsecs_t timeout = DEFAULT_INPUT_DISPATCHING_TIMEOUT;
    if (applicationHandle != nullptr) {
        timeout = applicationHandle->getDispatchingTimeout(timeout);
    }
    if (windowHandle != nullptr) {
        timeout = windowHandle->getDispatchingTimeout(timeout);
    }
    return std::make_optional(timeout);
}

// 事件分发时检查
void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LONG_LONG_MAX;
    {
        // ...
        if (!dispatchOnceInnerLocked(&nextWakeupTime)) {
            mLastDropReason = DropReason::DISABLED;
        }
        // 检查 ANR
        processAnrsLocked();
    }
    // ...
}

Input ANR 核心逻辑

void InputDispatcher::processAnrsLocked() {
    // 获取当前时间
    const nsecs_t currentTime = now();

    // 检查是否有未响应的窗口
    if (mNoFocusedWindowTimeoutTime.has_value() &&
            mAwaitedFocusedApplication != nullptr) {
        if (currentTime >= *mNoFocusedWindowTimeoutTime) {
            // 触发 ANR
            processNoFocusedWindowAnrLocked();
        }
    }

    // 检查已分发但未处理完的事件
    for (const auto& [token, state] : mAnrTracker.mTrackedAnrs) {
        if (currentTime >= state.timeoutTime) {
            // 事件处理超时,触发 ANR
            processConnectionUnresponsiveLocked(state.connection, "...");
        }
    }
}

2.5 ANR 信息收集流程

当 ANR 触发时,系统会收集关键信息用于分析:

// Android 11 源码:frameworks/base/services/core/java/com/android/server/am/AnrHelper.java

void appNotResponding(ProcessRecord anrProcess, String annotation) {
    // 步骤1:记录 ANR 基本信息
    synchronized (mAnrRecords) {
        mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName,
                aInfo, parentShortComponentName, parentProcess, aboveSystem,
                annotation));
    }

    // 步骤2:触发信息收集线程
    startAnrConsumerIfNeeded();
}

// 信息收集线程
class AnrConsumerThread extends Thread {
    @Override
    public void run() {
        AnrRecord r;
        while ((r = mAnrRecords.poll()) != null) {
            // 收集进程信息、堆栈等
            r.appNotResponding(mFirstPids);
        }
    }
}

ANR 信息收集内容

// Android 11 源码:frameworks/base/services/core/java/com/android/server/am/ProcessErrorStateRecord.java

void appNotResponding(...) {
    // 1. 收集 CPU 使用率
    mService.updateCpuStatsNow();

    // 2. dump 主线程堆栈到 /data/anr/traces.txt
    File tracesFile = ActivityManagerService.dumpStackTraces(
            firstPids, processCpuTracker, lastPids, nativePids);

    // 3. 记录到 EventLog
    EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
            app.processName, app.info.flags, annotation);

    // 4. 显示 ANR 弹窗
    Message msg = Message.obtain();
    msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
    mService.mUiHandler.sendMessage(msg);
}

三、实际应用

3.1 线上 ANR 监控方案

方案1:FileObserver 监听 /data/anr/

public class ANRFileObserver extends FileObserver {
    public ANRFileObserver(String path) {
        super(path, FileObserver.CLOSE_WRITE);
    }

    @Override
    public void onEvent(int event, String path) {
        if (path != null && path.contains("traces")) {
            // 读取并上报 ANR 信息
            String traces = readTraceFile(path);
            reportANR(traces);
        }
    }
}

局限性:Android 10+ 因权限限制无法直接访问 /data/anr/

方案2:WatchDog 主动检测

public class ANRWatchDog extends Thread {
    private static final int TIMEOUT = 5000;
    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private volatile long mLastTick = 0;

    private final Runnable mTicker = new Runnable() {
        @Override
        public void run() {
            mLastTick = System.currentTimeMillis();
        }
    };

    @Override
    public void run() {
        while (!isInterrupted()) {
            mLastTick = 0;
            mHandler.post(mTicker);

            try {
                Thread.sleep(TIMEOUT);
            } catch (InterruptedException e) {
                return;
            }

            // 检查主线程是否处理了消息
            if (mLastTick == 0) {
                // 主线程卡死,收集堆栈
                String stackTrace = getMainThreadStackTrace();
                reportANR(stackTrace);
            }
        }
    }
}

方案3:基于消息队列的监控(推荐)

public class LooperMonitor implements Printer {
    private long mStartTime = 0;
    private static final long THRESHOLD = 3000; // 3秒预警

    @Override
    public void println(String log) {
        if (log.startsWith(">>>>> Dispatching")) {
            mStartTime = System.currentTimeMillis();
        } else if (log.startsWith("<<<<< Finished")) {
            long cost = System.currentTimeMillis() - mStartTime;
            if (cost > THRESHOLD) {
                // 消息处理耗时过长,可能导致 ANR
                reportSlowMessage(cost, log);
            }
        }
    }
}

// 使用方式
Looper.getMainLooper().setMessageLogging(new LooperMonitor());

3.2 最佳实践

推荐做法

  1. 主线程避免耗时操作:IO、网络、数据库操作移到子线程
  2. 合理使用 IntentService/WorkManager:后台任务使用专用组件(详见 02-IntentService.md
  3. BroadcastReceiver 快速返回:onReceive() 中启动 Service 处理耗时逻辑
  4. 合理设置超时时间:自定义超时检测阈值略小于系统阈值
  5. 线上监控 + 预警:结合 WatchDog 和 Looper 日志监控

常见错误

  1. 主线程执行 SharedPreferences.commit() → 使用 apply() 异步写入
  2. BroadcastReceiver 中执行网络请求 → 启动 Service 或 WorkManager 处理
  3. Service onCreate() 中初始化大量数据 → 移到子线程或懒加载
  4. 锁竞争导致主线程等待 → 优化锁粒度,使用无锁数据结构

3.3 ANR 日志分析技巧

traces.txt 关键信息

----- pid 12345 at 2024-01-01 12:00:00 -----
Cmd line: com.example.app

"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x... self=0x...
  | sysTid=12345 nice=0 cgrp=default sched=0/0 handle=0x...
  | state=S schedstat=( 0 0 0 ) utm=100 stm=50 core=0 HZ=100
  | stack=0x7ff...-0x7ff... stackSize=8MB
  | held mutexes=
  at com.example.app.MainActivity.onClick(MainActivity.java:50)
  - waiting to lock <0x0abc...> (a java.lang.Object) held by thread 10
  at android.view.View.performClick(View.java:7125)
  ...

分析要点

  • Blocked/Waiting:主线程状态,表示被阻塞
  • waiting to lock:等待的锁对象
  • held by thread X:持有锁的线程
  • 堆栈顶部:阻塞发生的具体代码位置

四、面试真题解析

4.1 基础必答题

【高频题1】什么是 ANR?触发条件有哪些?

标准答案(30秒) : ANR 是 Application Not Responding 的缩写,当应用无法在规定时间内响应用户操作时触发。主要触发条件:输入事件 5 秒未处理、前台 Service 20 秒超时、前台广播 10 秒超时。系统通过 Handler 延迟消息机制实现超时检测。

深入展开: ANR 本质是系统对应用响应性的保护机制。系统在关键操作开始前埋入延迟消息(炸弹),操作完成后移除消息(拆弹),若消息未被移除并执行则触发 ANR(爆炸)。不同场景超时时间不同:后台 Service 200秒、后台广播 60秒。

面试官追问

  • 追问1:ANR 超时检测的消息发到哪个线程?

    • 答:发到 System Server 进程的 AMS Handler(主线程),不是应用进程。
  • 追问2:为什么后台服务超时时间比前台服务长?

    • 答:后台服务对用户体验影响小,给予更多执行时间;前台服务用户可感知,需要快速响应。

【高频题2】ANR 监控原理是什么?与 Handler 有什么关系?

标准答案(30秒) : ANR 监控基于 Handler 延迟消息机制,核心是"埋炸弹-拆炸弹"模式。以 Service 为例:启动时调用 scheduleServiceTimeoutLocked() 发送延迟消息,生命周期完成后调用 serviceDoneExecutingLocked() 移除消息,若超时未移除则执行 serviceTimeout() 触发 ANR。

深入展开: 系统服务(AMS/WMS)内部有自己的 Handler,用于调度各种超时检测。Service ANR 由 AMS 的 MainHandler 处理 SERVICE_TIMEOUT_MSG;Broadcast ANR 由 BroadcastQueue 的 Handler 处理 BROADCAST_TIMEOUT_MSG;Input ANR 由 InputDispatcher 在 native 层通过时间戳比对检测。

面试官追问

  • 追问1:如果 Service 的 onCreate() 执行了 19 秒,会触发 ANR 吗?

    • 答:前台 Service 不会(刚好低于 20 秒阈值),但实际中很危险,建议控制在 5 秒内。
  • 追问2:延迟消息用的是 sendMessageDelayed 还是 postDelayed?

    • 答:使用 sendMessageDelayed(),因为需要传递 Message 对象携带 ProcessRecord 等信息。

【高频题3】Input ANR 和 Service ANR 的检测机制有什么区别?

标准答案(30秒) : Service ANR 通过 Java 层 Handler 延迟消息实现,在 AMS 中调度;Input ANR 在 Native 层 InputDispatcher 中通过事件分发超时检测实现。Service ANR 是"到点检查",Input ANR 是"持续监控"未响应事件。

深入展开: Input ANR 检测更复杂:InputDispatcher 维护事件分发状态,dispatchOnce() 循环中调用 processAnrsLocked() 检查是否有超过 5 秒未处理的输入事件。如果应用窗口存在但无响应,会先等待;如果完全无焦点窗口,则立即计时。

面试官追问

  • 追问1:为什么 Input ANR 要在 Native 层实现?

    • 答:输入事件分发本身在 Native 层(InputFlinger),在同一层检测更高效,避免跨进程通信开销。
  • 追问2:Input ANR 的 5 秒是从什么时候开始计算的?

    • 答:从事件分发到目标窗口开始计算,而非事件产生时刻。

【高频题4】如何在线上监控 ANR?

标准答案(30秒) : 三种主流方案:1)FileObserver 监听 /data/anr/(Android 10+ 受限);2)WatchDog 线程定时向主线程发消息检测是否响应;3)Looper.setMessageLogging() 监控消息处理耗时。推荐组合使用 WatchDog + Looper 日志方案。

深入展开: WatchDog 原理是子线程定期 post Runnable 到主线程,若超时未执行说明主线程卡死。Looper 日志方案通过 println() 回调获取消息开始/结束时间,计算耗时。字节跳动 APM 等框架还会结合 Native 层信号量监控和主线程堆栈采样。

面试官追问

  • 追问1:WatchDog 方案的检测精度如何?

    • 答:精度取决于检测周期,通常设置 5 秒,可能存在最多 5 秒延迟。
  • 追问2:Looper 日志方案有什么缺点?

    • 答:有性能开销(字符串拼接),且只能检测到消息级别的卡顿,无法获取消息内部的具体堆栈。

【高频题5】ANR 发生时系统会收集哪些信息?

标准答案(30秒) : 系统会收集:1)主线程堆栈(traces.txt);2)CPU 使用率和各进程 CPU 占用;3)内存状态;4)最近的 EventLog;5)当前运行的服务和广播信息。这些信息保存在 /data/anr/ 目录下。

深入展开: 收集过程由 AnrHelper.appNotResponding() 触发,首先更新 CPU 统计,然后调用 dumpStackTraces() 收集目标进程及相关进程的堆栈。traces.txt 包含线程状态(Running/Blocked/Waiting)、持有的锁、等待的锁等关键信息,是分析 ANR 根因的核心依据。

面试官追问

  • 追问1:为什么要收集其他进程的堆栈?

    • 答:ANR 可能由其他进程引起(如 Binder 调用对端卡死、系统负载过高),需要全局信息定位。
  • 追问2:traces.txt 中如何判断是死锁导致的 ANR?

    • 答:查看 "waiting to lock" 和 "held by thread" 信息,如果形成环形等待则是死锁。

4.2 进阶加分题

【进阶题1】分析 Service ANR 从埋炸弹到引爆的完整源码流程

参考答案

完整流程涉及 AMS 和应用进程交互:

  1. 埋炸弹realStartServiceLocked()bumpServiceExecutingLocked()scheduleServiceTimeoutLocked()mHandler.sendMessageDelayed(SERVICE_TIMEOUT_MSG, timeout)
  2. 执行生命周期:AMS 通过 app.thread.scheduleCreateService() 跨进程调用应用的 ActivityThread.handleCreateService()
  3. 拆炸弹:应用执行完 onCreate() 后回调 AMS.serviceDoneExecuting()serviceDoneExecutingLocked()mHandler.removeMessages(SERVICE_TIMEOUT_MSG)
  4. 引爆:若超时未拆弹,MainHandler.handleMessage() 处理 SERVICE_TIMEOUT_MSGserviceTimeout()appNotResponding()

追问:bumpServiceExecutingLocked 为什么要用 executeNesting 计数?

  • 答:Service 可能有多个并发操作(create、bindService、startCommand),需要引用计数确保所有操作完成后才移除超时消息。

【进阶题2】Android 10+ 如何实现 ANR 监控?FileObserver 方案为何失效?

参考答案

Android 10 引入 Scoped Storage,应用无法访问 /data/anr/ 目录。替代方案:

  1. ApplicationExitInfo API(Android 11+)
ActivityManager am = getSystemService(ActivityManager.class);
List<ApplicationExitInfo> exitInfos = am.getHistoricalProcessExitReasons(
        packageName, 0, 10);
for (ApplicationExitInfo info : exitInfos) {
    if (info.getReason() == ApplicationExitInfo.REASON_ANR) {
        InputStream trace = info.getTraceInputStream();
        // 读取 ANR 堆栈
    }
}
  1. WatchDog + 信号量方案:监控线程通过信号量与主线程同步,超时则采集堆栈
  2. Native Signal Handler:监听 SIGQUIT 信号(系统 dump 堆栈时发送)

追问:ApplicationExitInfo 能获取到实时 ANR 吗?

  • 答:不能,只能获取历史 ANR 记录,适合冷启动时上报上次 ANR。实时监控需配合 WatchDog。

【进阶题3】为什么主线程 Looper.loop() 死循环不会导致 ANR?

参考答案

这与 ANR 定义有关:ANR 是"未在规定时间内响应用户操作",而非"主线程持续运行"。

  1. MessageQueue.next() 会阻塞:无消息时通过 epoll 机制休眠,不占用 CPU
  2. 事件驱动模型:用户操作产生事件 → 封装成 Message → 唤醒主线程处理 → 处理完继续休眠
  3. ANR 检测的是消息处理时长:只有某个消息处理超过阈值才会 ANR

loop() 循环本身是事件处理的载体,不是阻塞原因。详见 03-主线程Looper创建时机.md 中的 epoll 机制分析。


4.3 实战场景题

【场景题】线上反馈大量 ANR,但堆栈显示主线程在等待锁,如何定位?

问题:traces.txt 显示主线程状态为 Blocked,正在等待一个被子线程持有的锁。

答案思路

  1. 分析

    • 主线程等锁说明存在锁竞争
    • 找到持有锁的子线程(traces.txt 中 "held by thread X")
    • 分析子线程为何长时间持有锁
  2. 方案

    • 检查子线程是否在执行耗时操作(IO/网络)
    • 缩小锁粒度,只保护必要代码段
    • 使用 tryLock 带超时机制
    • 考虑使用无锁数据结构(ConcurrentHashMap 等)
  3. 实现

// 优化前:大锁
synchronized(lock) {
    data = loadFromNetwork();  // 耗时
    updateUI(data);
}

// 优化后:缩小锁粒度
data = loadFromNetwork();  // 不加锁
synchronized(lock) {
    updateUI(data);  // 只保护 UI 操作
}

追问

  • 如果是死锁导致的 ANR 怎么办?

    • 答:分析锁依赖关系,统一锁获取顺序,或使用 ReentrantLock.tryLock() 避免死等。
  • 如何预防此类问题?

    • 答:Code Review 关注锁使用;StrictMode 检测主线程耗时操作;线上监控锁等待时间。

五、对比与总结

5.1 不同 ANR 类型对比

类型超时时间检测位置检测机制触发消息/方法
Input ANR5秒Native InputDispatcher事件分发超时检测processAnrsLocked()
Service ANR20秒/200秒Java AMSHandler 延迟消息SERVICE_TIMEOUT_MSG
Broadcast ANR10秒/60秒Java BroadcastQueueHandler 延迟消息BROADCAST_TIMEOUT_MSG
Provider ANR10秒Java AMSHandler 延迟消息CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG

5.2 线上监控方案对比

方案优点缺点适用场景
FileObserver获取完整 tracesAndroid 10+ 失效低版本兼容
WatchDog实现简单精度有限通用场景
Looper 日志消息级粒度性能开销调试/预警
ApplicationExitInfo官方 API非实时历史记录上报
SIGQUIT 信号系统级实现复杂高精度需求

5.3 核心要点速记

一句话记忆: ANR 监控本质是 Handler 延迟消息的"埋炸弹-拆炸弹"模式,系统在操作前埋消息,完成后移除,超时未移除则引爆。

3个关键点

  1. Service/Broadcast ANR 通过 Java 层 Handler 延迟消息实现
  2. Input ANR 在 Native 层 InputDispatcher 通过时间戳检测
  3. ANR 信息收集包括堆栈、CPU、内存,保存在 /data/anr/

面试官最爱问

  1. ANR 超时时间分别是多少?(5秒/20秒/10秒)
  2. ANR 监控如何与 Handler 关联?(延迟消息机制)
  3. 线上如何监控 ANR?(WatchDog + Looper 日志)

六、关联知识点

前置知识

  • Handler 消息机制基础(详见:../01-Handler基础/)
  • MessageQueue 原理(详见:../02-MessageQueue/)
  • Looper 工作机制(详见:../03-Looper/)

后续扩展

  • 卡顿监控与优化(更细粒度的性能监控)
  • Native 层性能监控(PLT Hook、SIGQUIT 信号处理)
  • APM 系统设计(完整的性能监控体系)

相关文件

  • ./01-HandlerThread.md - 后台线程消息处理方案
  • ./02-IntentService.md - 后台任务执行组件
  • ./03-主线程Looper创建时机.md - 主线程消息循环启动时机