Android中的ANR:它是什么、原因及解决方法

作者: IT Sectr 发布日期: 2026-07-28 阅读时间: 9 分钟

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 — 主线程阻塞超过5秒时Android系统通知,导致“应用无响应”对话框
  • 主要原因 — 主线程阻塞(BroadcastReceiver、Service)、线程间死锁、ContentProvider中的长时间操作
  • 诊断 — 分析/data/anr/traces.txt、Android Studio Profiler、Firebase Performance Monitoring
  • 修复 — 将任务转移到WorkManager、使用Kotlin Coroutines与Dispatchers.IO、使用StrictMode早期检测
  • 预防 — 限制BroadcastReceiver时间为10秒、Service为20秒、ContentProvider为15秒

Android中的ANR是什么

ANR (Application Not Responding) — 是Android中的用户保护机制,当应用停止响应输入时激活。系统跟踪事件处理时间:如果BroadcastReceiver未在10秒内完成onReceive、Service未在20秒内从onCreate返回、ContentProvider未在15秒内响应——Android生成ANR。

ANR对用户的样子

当发生ANR时,Android在所有窗口之上显示系统对话框:“应用无响应。关闭还是等待?”用户可以关闭应用或等待其恢复。如果ANR频繁重复,用户将删除应用。Google Play在排序算法中考虑ANR率——有ANR的会话百分比。

ANR与iOS冻结的区别

iOS上没有带系统对话框的ANR对应物。相反,Apple使用Watchdog,它以代码0x8badf00d终止应用进程。用户看不到对话框——应用只是关闭到主屏幕。这使得Android上的ANR对用户更明显,但为系统提供了更多用于诊断的信息。

ANR的主要原因

当系统跟踪四种组件类型之一的超时时,就会发生ANR。每个组件都有自己的时间限制。

BroadcastReceiver中阻塞

BroadcastReceiver在主线程中执行。如果onReceive启动同步网络请求、长时间数据库写入操作或等待阻塞——10秒后出现ANR。解决方案:使用goAsync()和WorkManager进行后台处理。典型场景——从FCM接收推送通知并在Room中同步保存。

Service中的长时间操作

Service.onCreate和Service.onStartCommand有20秒的限制。如果服务在主线程中启动重型初始化(加载库、从网络读取配置)——ANR不可避免。使用IntentService(已过时)或WorkManager确保在后台线程中执行。

ContentProvider长时间初始化

ContentProvider.onCreate在调用Application.onCreate之前执行,有15秒的限制。如果提供程序执行数据库迁移、加载字典或从网络初始化SDK——这会在应用启动时引起ANR。解决方案:延迟初始化,将重型操作转移到WorkManager。

  • BroadcastReceiver — onReceive有10秒;使用goAsync()进行后台处理
  • Service — onCreate/onStartCommand有20秒;使用WorkManager或CoroutineWorker
  • ContentProvider — onCreate有15秒;将初始化转移到Application.onCreate并延迟启动
  • UI线程 — 5秒无事件处理;任何超过5秒的阻塞都会引起ANR

如何诊断ANR

Android提供了几种用于ANR分析的工具:从系统日志到专门的库。

分析traces.txt

每次ANR时,Android保存文件/data/anr/traces.txt,包含应用所有线程的堆栈转储。找到“main”线程——堆栈中的最后一个方法指示原因。典型模式:Thread.sleep()、InputStream.read()、BinderProxy.transact()。要从设备提取文件,请使用具有超级用户权限的adb。

Firebase Crashlytics与ANR报告

Firebase Crashlytics自动收集ANR并在仪表板中与跟踪一起显示。对于Android 11+,ANR报告随主线程的完整堆栈一起提供。集成需要添加依赖项并在Application.onCreate中初始化FirebaseApp。

Android Studio Profiler线程跟踪

Android Studio中的CPU Profiler允许您记录应用活动的跟踪,并查看哪些方法占用了CPU时间。启用“Record with method traces”并重现引起ANR的场景。在时间线上将看到冻结时刻主线程中执行了哪些方法。

Firebase Crashlytics集成以收集Android上ANR的示例:

kotlin
class App : Application() {
    override fun onCreate() {
        super.onCreate()
        FirebaseApp.initializeApp(this)
        FirebaseCrashlytics.getInstance()
            .setCrashlyticsCollectionEnabled(true)
        StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
            .detectAll()
            .penaltyLog()
            .build())
    }
}

修复ANR的方法

修复ANR主要是将所有长时间操作从主线程转移到后台线程。让我们看看每种组件类型的具体技术。

使用WorkManager处理后台任务

WorkManager — Google推荐的后台工作解决方案。它保证在后台线程中执行任务,同时考虑设备状态。与Service不同,WorkManager不会阻塞主线程,并且能够容忍应用重启。对于BroadcastReceiver,使用goAsync()并将结果PendingResult传递给WorkManager。

Kotlin Coroutines与正确的调度器

所有网络请求、数据库工作和文件操作使用Dispatchers.IO启动。主线程只能更新UI。使用viewModelScope在Activity销毁时自动取消协程。避免在任何上下文中使用runBlocking()——这是对当前线程的同步阻塞。

ContentProvider的延迟初始化

如果ContentProvider执行长时间初始化,请使用延迟加载机制:创建一个立即返回数据的提供程序,并通过WorkManager延迟启动重型初始化。这可以防止在应用启动时发生ANR,此时系统对延迟最敏感。

在Android中正确使用BroadcastReceiver与goAsync的示例:

kotlin
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预防

对抗ANR的最佳方法是通过工具和架构解决方案在开发阶段防止它们的出现。

使用StrictMode检测主线程阻塞

启用detectNetwork()和detectDiskReads()/detectDiskWrites()策略的StrictMode在开发阶段检测潜在ANR。在Debug构建中设置penaltyDeath——任何违规都将导致立即崩溃,开发者在提交之前就会看到问题。

Firebase Performance Monitoring用于生产指标

Firebase Performance跟踪关键操作的执行时间,并显示哪些场景超过ANR阈值。为每个屏幕和网络请求配置自定义跟踪。如果执行时间超过3秒——这是需要优化的潜在ANR。

网络和磁盘延迟测试

模拟慢速条件:通过iOS或Android Emulator上的Network Link Conditioner限制网络速度。通过慢速内存模拟减慢磁盘读取。ANR经常在这样的条件下出现,在开发者的快速设备上不可见。

  • BroadcastReceiver — 处理超过1秒时始终使用goAsync()
  • Service — 使用WorkManager或带后台调度器的CoroutineWorker替代
  • ContentProvider — 避免在onCreate中使用网络和数据库,使用带WorkManager的延迟初始化
  • UI线程 — Debug中使用penaltyDeath的StrictMode,生产监控使用Firebase Performance

常见问题

为什么ANR在Android上发生但在iOS上不发生?

Android明确跟踪主线程上事件处理时间并显示ANR对话框。iOS使用Watchdog,在冻结超过10-20秒时强制关闭应用。ANR是Android架构的一个特性,其中多个组件(BroadcastReceiver、Service)有严格的超时。

如何在无root的设备上找到traces.txt?

在Android 11+上,可以通过adb shell dumpsys dropbox --print data_app_anr获取ANR转储。在Android 10及更低版本上,无root无权限访问/data/anr/traces.txt。使用Firebase Crashlytics——它会自动为Android 11+收集ANR报告。

多大的ANR率被认为是可接受的?

Google Play推荐ANR率低于0.5%——即每1000次会话不超过5次ANR。ANR率超过1%的应用会在Google Play Console中收到警告,并可能被隐藏于推荐中。理想情况下,ANR率应低于0.1%。

协程能引起ANR吗?

协程本身不会阻塞线程。但如果在协程内部在主线程上执行runBlocking,或者协程使用Dispatchers.Main启动并执行长时间CPU操作——这将引起ANR。对于输入输出使用Dispatchers.IO,对于计算使用Dispatchers.Default。

如何在模拟器中测试ANR?

使用带“Slow Network”配置文件的Android Emulator,或编写一个在主线程上调用Thread.sleep(6000)的测试。通过Debug运行应用,5秒后您将看到ANR对话框。检查logcat中是否出现了带跟踪的ANR记录。

总结

  • ANR — 主线程阻塞超过5秒或组件超时时Android系统通知
  • 超时:BroadcastReceiver — 10秒,Service — 20秒,ContentProvider — 15秒,UI — 5秒
  • 诊断 — /data/anr/traces.txt、Firebase Crashlytics、Android Studio中的CPU Profiler
  • 修复 — WorkManager、goAsync()、Kotlin Coroutines与Dispatchers.IO、ContentProvider延迟初始化
  • 预防 — 带penaltyDeath的StrictMode、Firebase Performance Monitoring、网络延迟测试
  • Google Play推荐ANR率< 0.5%;当率> 1%时应用受到可见性限制
  • 建议:配置Firebase Crashlytics和Performance以收集生产中的ANR,并在超过0.3%阈值时设置警报

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读