一、什么是 ANR?
ANR(Application Not Responding) 是 Android 系统在应用程序主线程(UI
线程)被阻塞超过一定时间后触发的错误机制。此时系统会弹出一个对话框提示用户"应用无响应",用户可以选择等待或强制关闭应用。
ANR 的触发条件:
- 主线程阻塞超过阈值 :
1)输入事件(如点击、滑动) :5 秒内未处理完成。
2)BroadcastReceiver :前台广播 10 秒内未完成,后台广播 60 秒内未完成。
3)Service 启动或绑定:前台 Service 5 秒内未完成onCreate()或onStartCommand()。 - 主线程执行耗时操作:例如网络请求、数据库查询、文件读写等。
二、ANR 的根本原因
- 主线程被阻塞:主线程负责 UI 渲染和事件响应,若执行耗时操作,会直接导致界面卡顿或无响应。
- 过度复杂的布局或绘制:布局嵌套过深、频繁重绘等。
- 死锁或过度同步:线程间竞争锁资源,导致主线程等待。
三、如何避免 ANR?
1、主线程只处理 UI 和轻量操作
1)禁止在主线程执行以下操作 :
a)网络请求(HTTP/HTTPS)。
b) 数据库查询(SQLite)。
c) 大文件读写。
d) 复杂计算(如循环、加密解密)。
2)正确做法:使用子线程(如 Thread、HandlerThread)或异步框架(如 Kotlin 协程、RxJava、AsyncTask)。
2、使用异步框架处理耗时任务
1)Kotlin 协程:
2)Rxjava
3、 优化主线程任务
1)减少布局复杂度 :a) 使用 ConstraintLayout 减少嵌套层级;b) 避免 RelativeLayout 或 LinearLayout 的过度嵌套;c) 使用 ViewStub 延迟加载复杂布局。
2)避免过度绘制 :使用 Android Studio 的 Layout Inspector 和 GPU 渲染分析工具 检测绘制性能
3)分批处理数据:若需渲染大量数据(如列表),使用分页加载(Paging Library)或增量更新
4、 避免死锁和过度同步
1)减少锁竞争 :避免在主线程中等待子线程释放锁。
2)使用线程安全的数据结构 :如 ConcurrentHashMap、CopyOnWriteArrayList
3)谨慎使用 synchronized:尽量缩小同步代码块的范围
5、 监控 ANR 并分析日志
1)ANR 日志 :
a) 系统会在 /data/anr/ 目录下生成 traces.txt 文件,记录 ANR 时的线程堆栈
b) Android 11 及以上版本可通过 adb bugreport 获取更详细的日志
2)第三方监控工具 :
a) 使用 Firebase Crashlytics、Bugsnag 等工具捕获 ANR 事件。
b) 使用 ANR-WatchDog 库主动检测 ANR。
通过合理设计线程模型、使用现代异步框架,并结合性能分析工具,可显著降低 ANR 的发生概率。