1. ANR 概述
1.1 什么是 ANR
ANR 的全称是 Application Not Responding,也就是「应用无响应」。说白了,你的 App 在主线程里干了太多活,系统等不及了,就会弹出一个对话框告诉用户:「这个应用没有响应,你要不要关掉它?」。
我刚开始做 Android 开发时,第一次遇到 ANR 完全懵了。当时我正在调试一个图片上传功能,点了按钮后界面直接卡死,过了几秒系统弹了个对话框。我还以为是手机死机了……后来才知道,原来是我在 主线程 里直接做了网络上传。
为什么会这样?Android 系统为了保障用户体验,给主线程设定了一个「容忍极限」。一旦主线程在规定时间内没有处理完该做的事,系统就会判定为 ANR。你想想看,如果你点了一个按钮,等了 10 秒都没反应,你是不是也想关掉这个 App?
核心要点:ANR 的本质是主线程被阻塞,导致无法及时处理用户交互或系统请求。
1.2 ANR 的类型
Android 系统定义了四种 ANR 场景。每种场景的触发方式和超时时间都不一样。我在项目中基本都碰过一遍,印象太深了。
- 输入事件 ANR —— 用户触摸或按键后,系统在 5 秒内没能把事件分发到 Activity 或 View 并处理完成。我记得有一次排查一个线上问题,用户反馈「点登录按钮没反应」,看了 traces 文件才发现是主线程在等一个锁,锁被后台线程占着不放。
-
广播 ANR —— BroadcastReceiver 的
onReceive()方法执行时间过长。前台广播超时 10 秒,后台广播超时 60 秒。我踩过一个坑:在onReceive()里直接调了SharedPreferences的繁重写入,数据量大时直接卡死。 -
服务 ANR —— Service 的生命周期方法(如
onCreate()、onStartCommand()等)超过 20 秒没执行完。说实话,这个在普通 App 里不太常见,但如果你在服务里做了大批量数据库操作,就很容易触发。 -
ContentProvider ANR —— ContentProvider 的操作超过 10 秒没响应。我在项目中就遇到过:另一个 App 通过 ContentResolver 查询我们的 ContentProvider,结果我们在
query()里做了复杂的跨进程数据合并,直接 ANR。
个人小习惯:我每次写 BroadcastReceiver 和 Service 时,都会在入口方法里加一行日志,记录开始时间。如果发现执行超过 2 秒,我就会考虑用
goAsync() 或者直接扔到 WorkManager 里去做。
1.3 触发条件与系统超时时间
ANR 的触发条件,说到底只有一条:主线程被阻塞,导致无法及时处理下一个消息。但具体「多久算不及时」,系统针对不同场景有不同阈值。
除了阈值,还有几个常见的「帮凶」:
- 主线程直接做网络请求 —— 这是最经典的 ANR 原因,没有之一。
- 主线程持有锁,被其他线程长时间占用 —— 我遇到过死锁导致的 ANR,两个线程互相等对方的锁,主线程也插不进去。
- IPC 调用等待 —— 比如主线程跨进程调用 ContentProvider 或 Service,对方响应慢就会连带卡住你。
- 系统负载过高 —— CPU 被占满,主线程抢不到时间片。这种情况虽然少见,但在低端机上确实会发生。
我曾经踩过的坑:有一次我在
Activity.onCreate() 里直接调用了 PackageManager.queryIntentActivities(),这个方法在部分系统版本上会触发跨进程通信,如果系统服务繁忙,主线程就会卡住。后来我把这个调用移到了异步线程,问题瞬间消失。
一句话总结:ANR 是 Android 系统给主线程画的一条红线。只要你在主线程里做了任何可能超过阈值的事,系统就会翻脸。理解这四种 ANR 类型和它们的超时时间,是后续优化工作的基础。
一个小建议:刚接触 ANR 的同学,可以先用
adb shell dumpsys dropbox --print data_app_anr 拉取设备上的 ANR 日志来看。读上三五份 traces 文件,你就能直观感受到 ANR 长什么样了。