文章目录
-
- [一、ANR 概述](#一、ANR 概述)
- [二、ANR 的三种类型及触发条件](#二、ANR 的三种类型及触发条件)
- [三、ANR 的核心原理(重点)](#三、ANR 的核心原理(重点))
-
- [3.1 Input ANR 机制](#3.1 Input ANR 机制)
- [3.2 Service/Broadcast ANR 机制](#3.2 Service/Broadcast ANR 机制)
- [3.3 主线程阻塞的底层原因](#3.3 主线程阻塞的底层原因)
- [四、ANR 的触发流程(源码级别)](#四、ANR 的触发流程(源码级别))
- [五、ANR 的排查思路(面试加分项)](#五、ANR 的排查思路(面试加分项))
- [六、常见导致 ANR 的编码错误(面试应答示例)](#六、常见导致 ANR 的编码错误(面试应答示例))
- 七、优化与预防(展示工程能力)
- 八、面试中可能遇到的追问
- 九、总结一句话(面试金句)
- 十、扩展阅读
一、ANR 概述
定义:ANR(Application Not Responding)即应用无响应,当应用的主线程在特定时间内未能处理完某个事件时,系统弹出的对话框。
核心原因:主线程被阻塞,无法及时响应输入事件或组件生命周期回调。
二、ANR 的三种类型及触发条件
| 类型 | 触发场景 | 超时时间 | 关键组件 |
|---|---|---|---|
| Input ANR | 按键/触摸事件未响应 | 5秒 | InputDispatcher |
| Service ANR | Service 启动/绑定超时 | 20秒(前台)/ 200秒(后台) | ActivityManagerService |
| Broadcast ANR | 广播接收者 onReceive 未执行完 | 10秒 | BroadcastQueue |
| ContentProvider ANR | ContentProvider 发布超时 | 10秒 | ContentProviderPublisher |
注:Android 12 后对后台广播限制更严,前台广播超时仍为 10 秒。
三、ANR 的核心原理(重点)
3.1 Input ANR 机制
- InputDispatcher 负责分发输入事件,会记录每个事件的开始时间。
- 事件发送给应用主线程后,主线程通过 Looper 循环处理。
- 如果事件处理完成后,应用未在 5 秒内 通知 InputDispatcher 处理完成,InputDispatcher 会检查应用主线程是否阻塞。
- 阻塞判定:
inputDispatchingTimeout超时 && 应用主线程正在处理上一个事件或处于等待状态 → 触发 ANR。
3.2 Service/Broadcast ANR 机制
- AMS 在启动 Service 或发送广播时,会通过 Handler 发送一个延时消息(超时消息)。
- 如果组件在超时前正常完成,会移除该延时消息。
- 超时消息到时触发,AMS 检查组件状态 → 若未完成 → 触发 ANR。
源码关键路径:
ActiveServices.scheduleServiceTimeoutLocked()→SERVICE_TIMEOUT_MSGBroadcastQueue.processNextBroadcast()→BROADCAST_TIMEOUT_MSG
3.3 主线程阻塞的底层原因
Android 应用运行基于 消息驱动模型(MessageQueue + Looper)。主线程不断从 MessageQueue 中取消息执行。如果某消息执行耗时过长,后续消息(包括触摸事件、生命周期的下一动作)无法及时处理,就会导致 ANR。
四、ANR 的触发流程(源码级别)
- 系统服务(AMS/InputDispatcher)检测到超时。
- 调用
appNotResponding()方法。 - 收集进程状态:主线程堆栈(
traces.txt)、CPU 使用情况、内存信息。 - 向 SystemServer 中的
Watchdog报告。 - 弹出 ANR 对话框(或后台直接杀进程,取决于场景)。
五、ANR 的排查思路(面试加分项)
| 步骤 | 操作 | 关键命令/文件 |
|---|---|---|
| 1 | 抓取 ANR 日志 | adb pull /data/anr/traces.txt |
| 2 | 查看主线程堆栈 | 搜索 "main" prio= 找到主线程,关注 waiting、sleeping、monitor 状态 |
| 3 | 确认 ANR 类型 | 从 logcat 中搜索 ANR in 或 Input dispatching timed out |
| 4 | 分析卡顿点 | 堆栈中的业务代码行号,定位耗时操作 |
| 5 | 系统负载检查 | logcat 中搜索 CPU usage、load average |
六、常见导致 ANR 的编码错误(面试应答示例)
- ❌ 主线程直接执行网络请求、数据库批量操作
- ❌ 主线程持有锁并等待子线程的某个条件(死锁)
- ❌ 使用
Thread.sleep()或wait()在主线程 - ❌ BroadcastReceiver 的
onReceive()中执行耗时操作(应使用goAsync()或启动 Service) - ❌ 多进程下的
ContentProvider调用在主线程同步等待
七、优化与预防(展示工程能力)
- 使用 StrictMode 检测主线程 IO/网络
- 异步处理:
AsyncTask(已废弃)、HandlerThread、Coroutines - 启动优化:延迟初始化、
Application.onCreate轻量化 - 使用
Systrace/Perfetto分析主线程卡顿点 - 采用
ViewStub、异步 Inflate 优化布局加载
八、面试中可能遇到的追问
Q1:ANR 和 Crash 的区别?
- Crash 是未捕获异常导致进程中止;ANR 是进程活着但无响应,系统会弹框让用户选择「等待」或「关闭」。
Q2:能否避免 ANR 对话框弹出?
- 不能完全避免,但可以通过优化保证应用始终响应。系统设置的
ANR弹框默认开启,但车机场景可通过Settings.Global.ANR_SHOW_NOTIFICATION等配置关闭(需系统权限)。
Q3:Service 的 ANR 超时时间为什么比 Broadcast 长?
- Service 通常是后台长时间运行操作,允许更长的启动时间;Broadcast 期望快速完成(沾到主线程),超时更短。
Q4:traces.txt 文件中没有主线程堆栈怎么办?
- 尝试手动抓取:
adb shell kill -3 <pid>生成当前线程堆栈。如果依然没有,可能是进程已死或权限问题。
九、总结一句话(面试金句)
ANR 是 Android 系统为保证应用响应性而设计的软限流机制,本质是主线程阻塞导致消息处理超时,通过
InputDispatcher和AMS的延时消息机制触发,排查的核心是分析/data/anr/traces.txt中的主线程调用栈。
建议背诵:类型、超时时间、触发机制、排查命令、常见原因。结合一两个实际项目中的 ANR 案例说明会更出彩。
十、扩展阅读
分析路线:触发ANR的过程可分为三个步骤: 埋炸弹, 拆炸弹, 引爆炸弹