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() 执行超时 |
| ContentProvider | 10秒 | 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 最佳实践
推荐做法:
- 主线程避免耗时操作:IO、网络、数据库操作移到子线程
- 合理使用 IntentService/WorkManager:后台任务使用专用组件(详见
02-IntentService.md) - BroadcastReceiver 快速返回:onReceive() 中启动 Service 处理耗时逻辑
- 合理设置超时时间:自定义超时检测阈值略小于系统阈值
- 线上监控 + 预警:结合 WatchDog 和 Looper 日志监控
常见错误:
- 主线程执行 SharedPreferences.commit() → 使用 apply() 异步写入
- BroadcastReceiver 中执行网络请求 → 启动 Service 或 WorkManager 处理
- Service onCreate() 中初始化大量数据 → 移到子线程或懒加载
- 锁竞争导致主线程等待 → 优化锁粒度,使用无锁数据结构
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 和应用进程交互:
- 埋炸弹:
realStartServiceLocked()→bumpServiceExecutingLocked()→scheduleServiceTimeoutLocked()→mHandler.sendMessageDelayed(SERVICE_TIMEOUT_MSG, timeout) - 执行生命周期:AMS 通过
app.thread.scheduleCreateService()跨进程调用应用的ActivityThread.handleCreateService() - 拆炸弹:应用执行完
onCreate()后回调AMS.serviceDoneExecuting()→serviceDoneExecutingLocked()→mHandler.removeMessages(SERVICE_TIMEOUT_MSG) - 引爆:若超时未拆弹,
MainHandler.handleMessage()处理SERVICE_TIMEOUT_MSG→serviceTimeout()→appNotResponding()
追问:bumpServiceExecutingLocked 为什么要用 executeNesting 计数?
- 答:Service 可能有多个并发操作(create、bindService、startCommand),需要引用计数确保所有操作完成后才移除超时消息。
【进阶题2】Android 10+ 如何实现 ANR 监控?FileObserver 方案为何失效?
参考答案:
Android 10 引入 Scoped Storage,应用无法访问 /data/anr/ 目录。替代方案:
- 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 堆栈
}
}
- WatchDog + 信号量方案:监控线程通过信号量与主线程同步,超时则采集堆栈
- Native Signal Handler:监听 SIGQUIT 信号(系统 dump 堆栈时发送)
追问:ApplicationExitInfo 能获取到实时 ANR 吗?
- 答:不能,只能获取历史 ANR 记录,适合冷启动时上报上次 ANR。实时监控需配合 WatchDog。
【进阶题3】为什么主线程 Looper.loop() 死循环不会导致 ANR?
参考答案:
这与 ANR 定义有关:ANR 是"未在规定时间内响应用户操作",而非"主线程持续运行"。
- MessageQueue.next() 会阻塞:无消息时通过 epoll 机制休眠,不占用 CPU
- 事件驱动模型:用户操作产生事件 → 封装成 Message → 唤醒主线程处理 → 处理完继续休眠
- ANR 检测的是消息处理时长:只有某个消息处理超过阈值才会 ANR
loop() 循环本身是事件处理的载体,不是阻塞原因。详见 03-主线程Looper创建时机.md 中的 epoll 机制分析。
4.3 实战场景题
【场景题】线上反馈大量 ANR,但堆栈显示主线程在等待锁,如何定位?
问题:traces.txt 显示主线程状态为 Blocked,正在等待一个被子线程持有的锁。
答案思路:
-
分析:
- 主线程等锁说明存在锁竞争
- 找到持有锁的子线程(traces.txt 中 "held by thread X")
- 分析子线程为何长时间持有锁
-
方案:
- 检查子线程是否在执行耗时操作(IO/网络)
- 缩小锁粒度,只保护必要代码段
- 使用 tryLock 带超时机制
- 考虑使用无锁数据结构(ConcurrentHashMap 等)
-
实现:
// 优化前:大锁
synchronized(lock) {
data = loadFromNetwork(); // 耗时
updateUI(data);
}
// 优化后:缩小锁粒度
data = loadFromNetwork(); // 不加锁
synchronized(lock) {
updateUI(data); // 只保护 UI 操作
}
追问:
-
如果是死锁导致的 ANR 怎么办?
- 答:分析锁依赖关系,统一锁获取顺序,或使用 ReentrantLock.tryLock() 避免死等。
-
如何预防此类问题?
- 答:Code Review 关注锁使用;StrictMode 检测主线程耗时操作;线上监控锁等待时间。
五、对比与总结
5.1 不同 ANR 类型对比
| 类型 | 超时时间 | 检测位置 | 检测机制 | 触发消息/方法 |
|---|---|---|---|---|
| Input ANR | 5秒 | Native InputDispatcher | 事件分发超时检测 | processAnrsLocked() |
| Service ANR | 20秒/200秒 | Java AMS | Handler 延迟消息 | SERVICE_TIMEOUT_MSG |
| Broadcast ANR | 10秒/60秒 | Java BroadcastQueue | Handler 延迟消息 | BROADCAST_TIMEOUT_MSG |
| Provider ANR | 10秒 | Java AMS | Handler 延迟消息 | CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG |
5.2 线上监控方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| FileObserver | 获取完整 traces | Android 10+ 失效 | 低版本兼容 |
| WatchDog | 实现简单 | 精度有限 | 通用场景 |
| Looper 日志 | 消息级粒度 | 性能开销 | 调试/预警 |
| ApplicationExitInfo | 官方 API | 非实时 | 历史记录上报 |
| SIGQUIT 信号 | 系统级 | 实现复杂 | 高精度需求 |
5.3 核心要点速记
一句话记忆: ANR 监控本质是 Handler 延迟消息的"埋炸弹-拆炸弹"模式,系统在操作前埋消息,完成后移除,超时未移除则引爆。
3个关键点:
- Service/Broadcast ANR 通过 Java 层 Handler 延迟消息实现
- Input ANR 在 Native 层 InputDispatcher 通过时间戳检测
- ANR 信息收集包括堆栈、CPU、内存,保存在 /data/anr/
面试官最爱问:
- ANR 超时时间分别是多少?(5秒/20秒/10秒)
- ANR 监控如何与 Handler 关联?(延迟消息机制)
- 线上如何监控 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 - 主线程消息循环启动时机