ANR(Application Not Responding)是Android的系统通知,当应用超过5秒未响应用户输入时出现。根据Android Developers,主要原因是主线程上的长时间操作阻塞了触摸处理和界面渲染。理解ANR机制对于每个Android开发者创建响应式应用至关重要。
要点
ANR(Application Not Responding)— 是Android操作系统的一个对话框,当应用停止响应用户输入时出现。系统通过InputDispatcher监视事件处理时间:如果触摸或按钮按下在5秒内未被处理,Android会显示一个对话框,建议关闭或等待应用。
ANR机制保护用户体验不受冻结应用的影响。Android不允许一个应用阻塞整个系统 — 与桌面操作系统不同,移动平台强制限制事件处理时间。BroadcastReceiver有10秒的限制,前台服务有20秒的限制。
ANR不是代码中的异常 — 它是Linux进程级别的系统机制。Android向进程发送SIGQUIT信号,系统将所有线程的调用堆栈保存到traces.txt文件。开发者不是通过catch异常获取ANR,而是在应用重启后获得报告。从Android 11开始,API ApplicationExitInfo允许以编程方式获取进程终止原因,包括ANR — 这简化了统计收集,无需手动解析traces.txt。
五类操作在Android应用中稳定导致ANR。每一类都会阻塞主线程,阻止系统处理输入事件和屏幕重绘。
同步HTTP请求在UI线程上执行是初学者最常见的ANR原因。即使是对服务器的快速请求也可能需要1–3秒,而在连接不良时 — 30秒或更长。Android从API 11开始明确禁止主线程上的网络操作,抛出NetworkOnMainThreadException。
使用Coroutines或RxJava进行异步调用。使用Dispatchers.IO调度器的协程在后台线程执行请求,并通过Dispatchers.Main将结果传递到主线程。这完全消除了网络操作对UI线程的阻塞。
fun fetchUserData() {
CoroutineScope(Dispatchers.Main).launch {
val result = withContext(Dispatchers.IO) {
api.getUserData() // 后台操作
}
updateUI(result) // 主线程上的结果
}
}
处理大数据集、解析JSON或XML、直接在主线程上操作bitmap — 是第二常见的ANR原因。即使UI线程连续工作300毫秒而不返回事件循环,也会引起明显的渲染延迟,而5秒的边界值则被记录为ANR。
WorkManager和后台服务旨在将繁重计算移出主线程。使用AsyncTask(已弃用)、ListenableFuture或Kotlin Flow分块传输数据,不阻塞UI。
死锁发生在两个线程持有锁并相互等待时。如果其中一个线程是主线程,系统正好在5秒后记录ANR。从UI线程调用的Thread.join()、CountDownLatch.await()和synchronized块存在阻塞风险。
避免在主线程上进行任何阻塞操作。使用ConcurrentHashMap代替synchronized,使用协程的async/await代替Thread.join()。此规则适用于Android中的任何语言:Java、Kotlin或通过JNI的C++。
BroadcastReceiver默认在主线程上执行。如果onReceive()占用超过10秒,Android会显示ANR。在onReceive内部从数据库或网络加载数据是通往冻结的保证路径。
在BroadcastReceiver内部使用goAsync()切换到后台线程,或者使用getBackgroundBroadcastReceiver()注册registerReceiver。这允许在不阻塞UI的情况下处理事件。
对ContentProvider的繁重查询或直接在UI线程上操作SQLite — 是较不明显但常见的ANR原因。在数据库迁移或批量插入数千条记录时,执行时间可能超过5秒的限制。
将所有数据库操作通过Room的suspend函数移至后台线程。Room会自动检查查询是否在主线程上执行,并在违规时抛出异常。
诊断ANR与调试普通异常不同 — 你无法在try-catch中捕获ANR。主要信息来源是traces.txt文件,Android在冻结时创建该文件。
traces.txt包含ANR时刻应用所有线程的调用堆栈。要从真实设备读取文件,执行adb bugreport命令,该命令收集完整的系统报告,包括最近的所有ANR。对于模拟器,文件位于/data/anr/traces.txt。调用堆栈显示阻塞时刻主线程上执行的方法。
adb bugreport bugreport.zip
unzip -p bugreport.zip "*traces*" > traces.txt
Google Play Console提供ANR & Crash部分,包含聚合报告和错误频率。每个ANR显示调用堆栈和按设备的统计信息:型号、Android版本、地区。这有助于发现依赖特定设备或系统版本的ANR。
从2021年开始,Android Studio的分析器包含ANR Watchdog。如果主线程超过阈值时间未响应,它会自动记录线程转储。该工具显示事件时间线:启动了哪些操作,执行了哪些方法,以及在哪个阶段发生了阻塞。
预防ANR基于一个基本原则:主线程应该只处理UI事件。任何超过16毫秒(一帧的时间)的操作都应在后台线程中执行。
StrictMode — Android内置工具,用于在开发阶段检测潜在的ANR。在Application.onCreate()中启用它,设置磁盘和网络操作的标志。违规时,StrictMode抛出异常或写入logcat。
if (BuildConfig.DEBUG) {
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
.let { StrictMode.setThreadPolicy(it) }
}
Kotlin Coroutines — 现代Android应用中异步工作的标准方式。核心技巧:I/O操作在Dispatchers.IO上执行,结果传递到Dispatchers.Main以更新UI。对于Flow类场景,对CPU密集型任务使用Dispatchers.Default。
RxJava在遗留项目中仍然流行。subscribeOn(Schedulers.io())和observeOn(AndroidSchedulers.mainThread())是预防ANR的最基本配置。同样规则:没有任何Observable或Flowable应该从主线程发射数据。
Firebase Crashlytics从SDK 18.4.0版本开始支持开箱即用的ANR监控。对于Android 11+,Crashlytics使用系统API ApplicationExitInfo,提供准确的终止原因:ANR、Crash或系统杀死。启用包含屏幕和状态参数的自定义键以进行上下文分析。
五个工具覆盖了ANR处理的所有阶段:从工作站调试到生产监控。每个工具解决自己的问题,并为不同场景提供数据。
| 工具 | 用途 | 数据格式 |
|---|---|---|
| StrictMode | 开发阶段检测 | Logcat / Exception |
| ANR Watchdog(Android Studio) | 实时跟踪 | Thread dump + timeline |
| Google Play Console | 聚合统计 | ANR rate + stack traces |
| Firebase Crashlytics | 生产监控 | ApplicationExitInfo |
| adb bugreport | 完整系统报告 | traces.txt + logcat + dmesg |
每个工具都有自己的定位:StrictMode在早期捕获明显违规;Crashlytics显示用户中实际的ANR频率;而adb bugreport为复杂情况提供最完整的图像。组合使用它们以获得全面覆盖。
Firebase Performance监控UI线程的响应时间,并自动为可疑的长时间操作创建跟踪。如果主线程阻塞超过500毫秒,Performance会记录一个包含罪魁祸首方法名称的自定义跟踪。这允许在用户参与之前、在ANR变得严重之前发现ANR场景。
与Firebase Crashlytics的集成提供完整图像:Performance显示ANR之前的减速,而Crashlytics显示冻结事件本身。在Firebase Console中设置ANR rate超过0.1%的警报,你将在大规模用户投诉之前收到新问题的通知。
常见问题
ANR — 是应用不响应但仍保留在内存中的冻结。Crash — 是完全崩溃,进程退出。ANR可以「幸存」,如果系统或用户等待响应,而Crash总是终止应用。
不能。ANR不是Java/Kotlin异常,而是进程级别的系统信号(SIGQUIT)。开发者无法在应用代码中处理它。响应ANR的唯一方式是在重启后分析报告。
设备性能、Android版本、CPU负载和后台进程数量都会影响ANR的概率。在性能较弱的设备上,同样的操作可能需要2–3倍的时间,超过5秒的限制。
普通BroadcastReceiver的onReceive()为10秒。前台服务的限制为20秒,而ContentProvider没有明确限制,但主线程阻塞超过5秒仍然会导致ANR。
在所有debug构建中启用StrictMode,通过Firebase Crashlytics添加监控,并在ANR发生时使用adb bugreport。不规律的ANR通常与竞态条件或特定网络状态有关。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。