ANR (Application Not Responding) — 是Android系统通知,当应用在5秒内未响应输入时出现。与glitch(不阻塞UI的逻辑错误)和lag(不完全停止的缓慢)不同,ANR是由操作系统记录的关键故障:Android会显示“应用无响应”对话框,提供关闭或等待选项。根据 Android Vitals Documentation,ANR率超过0.5%的应用在Google Play中评级较低,并可能被隐藏于推荐中。诊断包括分析/data/anr/traces.txt、使用StrictMode和主线程性能分析。
要点
ANR (Application Not Responding) — 是Android中的用户保护机制,当应用停止响应输入时激活。系统跟踪事件处理时间:如果BroadcastReceiver未在10秒内完成onReceive、Service未在20秒内从onCreate返回、ContentProvider未在15秒内响应——Android生成ANR。
当发生ANR时,Android在所有窗口之上显示系统对话框:“应用无响应。关闭还是等待?”用户可以关闭应用或等待其恢复。如果ANR频繁重复,用户将删除应用。Google Play在排序算法中考虑ANR率——有ANR的会话百分比。
iOS上没有带系统对话框的ANR对应物。相反,Apple使用Watchdog,它以代码0x8badf00d终止应用进程。用户看不到对话框——应用只是关闭到主屏幕。这使得Android上的ANR对用户更明显,但为系统提供了更多用于诊断的信息。
当系统跟踪四种组件类型之一的超时时,就会发生ANR。每个组件都有自己的时间限制。
BroadcastReceiver在主线程中执行。如果onReceive启动同步网络请求、长时间数据库写入操作或等待阻塞——10秒后出现ANR。解决方案:使用goAsync()和WorkManager进行后台处理。典型场景——从FCM接收推送通知并在Room中同步保存。
Service.onCreate和Service.onStartCommand有20秒的限制。如果服务在主线程中启动重型初始化(加载库、从网络读取配置)——ANR不可避免。使用IntentService(已过时)或WorkManager确保在后台线程中执行。
ContentProvider.onCreate在调用Application.onCreate之前执行,有15秒的限制。如果提供程序执行数据库迁移、加载字典或从网络初始化SDK——这会在应用启动时引起ANR。解决方案:延迟初始化,将重型操作转移到WorkManager。
Android提供了几种用于ANR分析的工具:从系统日志到专门的库。
每次ANR时,Android保存文件/data/anr/traces.txt,包含应用所有线程的堆栈转储。找到“main”线程——堆栈中的最后一个方法指示原因。典型模式:Thread.sleep()、InputStream.read()、BinderProxy.transact()。要从设备提取文件,请使用具有超级用户权限的adb。
Firebase Crashlytics自动收集ANR并在仪表板中与跟踪一起显示。对于Android 11+,ANR报告随主线程的完整堆栈一起提供。集成需要添加依赖项并在Application.onCreate中初始化FirebaseApp。
Android Studio中的CPU Profiler允许您记录应用活动的跟踪,并查看哪些方法占用了CPU时间。启用“Record with method traces”并重现引起ANR的场景。在时间线上将看到冻结时刻主线程中执行了哪些方法。
Firebase Crashlytics集成以收集Android上ANR的示例:
class App : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
FirebaseCrashlytics.getInstance()
.setCrashlyticsCollectionEnabled(true)
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
}
修复ANR主要是将所有长时间操作从主线程转移到后台线程。让我们看看每种组件类型的具体技术。
WorkManager — Google推荐的后台工作解决方案。它保证在后台线程中执行任务,同时考虑设备状态。与Service不同,WorkManager不会阻塞主线程,并且能够容忍应用重启。对于BroadcastReceiver,使用goAsync()并将结果PendingResult传递给WorkManager。
所有网络请求、数据库工作和文件操作使用Dispatchers.IO启动。主线程只能更新UI。使用viewModelScope在Activity销毁时自动取消协程。避免在任何上下文中使用runBlocking()——这是对当前线程的同步阻塞。
如果ContentProvider执行长时间初始化,请使用延迟加载机制:创建一个立即返回数据的提供程序,并通过WorkManager延迟启动重型初始化。这可以防止在应用启动时发生ANR,此时系统对延迟最敏感。
在Android中正确使用BroadcastReceiver与goAsync的示例:
class FcmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val pendingResult = goAsync()
WorkManager.getInstance(context!!)
.enqueue(OneTimeWorkRequest.from(NotificationWorker::class.java))
pendingResult.finish()
}
}
对抗ANR的最佳方法是通过工具和架构解决方案在开发阶段防止它们的出现。
启用detectNetwork()和detectDiskReads()/detectDiskWrites()策略的StrictMode在开发阶段检测潜在ANR。在Debug构建中设置penaltyDeath——任何违规都将导致立即崩溃,开发者在提交之前就会看到问题。
Firebase Performance跟踪关键操作的执行时间,并显示哪些场景超过ANR阈值。为每个屏幕和网络请求配置自定义跟踪。如果执行时间超过3秒——这是需要优化的潜在ANR。
模拟慢速条件:通过iOS或Android Emulator上的Network Link Conditioner限制网络速度。通过慢速内存模拟减慢磁盘读取。ANR经常在这样的条件下出现,在开发者的快速设备上不可见。
常见问题
Android明确跟踪主线程上事件处理时间并显示ANR对话框。iOS使用Watchdog,在冻结超过10-20秒时强制关闭应用。ANR是Android架构的一个特性,其中多个组件(BroadcastReceiver、Service)有严格的超时。
在Android 11+上,可以通过adb shell dumpsys dropbox --print data_app_anr获取ANR转储。在Android 10及更低版本上,无root无权限访问/data/anr/traces.txt。使用Firebase Crashlytics——它会自动为Android 11+收集ANR报告。
Google Play推荐ANR率低于0.5%——即每1000次会话不超过5次ANR。ANR率超过1%的应用会在Google Play Console中收到警告,并可能被隐藏于推荐中。理想情况下,ANR率应低于0.1%。
协程本身不会阻塞线程。但如果在协程内部在主线程上执行runBlocking,或者协程使用Dispatchers.Main启动并执行长时间CPU操作——这将引起ANR。对于输入输出使用Dispatchers.IO,对于计算使用Dispatchers.Default。
使用带“Slow Network”配置文件的Android Emulator,或编写一个在主线程上调用Thread.sleep(6000)的测试。通过Debug运行应用,5秒后您将看到ANR对话框。检查logcat中是否出现了带跟踪的ANR记录。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。