当 Android 应用的 UI 线程被阻塞时间过长时,系统会发送“应用无响应”(ANR) 错误。本页面介绍了不同类型的 ANR、如何诊断它们以及修复它们的建议。所列出的所有默认超时时间范围均适用于 AOSP 和 Pixel 设备;这些时间可能因 OEM 而异。
请记住,在确定 ANR 的原因时,区分系统问题和应用问题会很有帮助。
当系统处于不良状态时,以下问题可能会导致 ANR:
- 系统服务器中的瞬时问题会导致通常快速的 Binder 调用变得缓慢。
- 系统服务器问题和高设备负载会导致应用线程无法调度。
如果可以,区分系统问题和应用问题的一个好方法是使用 Perfetto 跟踪。
- 通过查看 Perfetto 中的线程状态轨迹,了解应用的 main 线程是否被调度,判断其是否正在运行或可运行。
- 查看
system_server
线程,查找锁竞争等问题。 - 对于缓慢的 Binder 调用,如果有回复线程,请查看该线程以了解其缓慢的原因。
输入分发超时
输入分发 ANR 发生在应用的主线程未能及时响应输入事件(例如滑动或按键)时。由于输入分发超时发生时应用处于前台,因此它们几乎总是对用户可见,并且非常重要加以缓解。
默认超时时间:5 秒。
输入分发 ANR 通常由主线程上的问题引起。如果主线程在等待获取锁时被阻塞,则持有锁的线程也可能涉及其中。
为避免输入分发 ANR,请遵循以下最佳实践:
- 不要在主线程上执行阻塞或长时间运行的操作。考虑使用
StrictMode
来捕获主线程上的意外活动。 - 最大限度地减少主线程与其他线程之间的锁竞争。
- 最大限度地减少主线程上的非 UI 工作,例如处理广播或运行服务时。
常见原因
以下是输入分发 ANR 的一些常见原因和建议的修复方法。
原因 | 发生情况 | 建议修复 |
---|---|---|
缓慢的 Binder 调用 | 主线程执行长时间的同步 Binder 调用。 | 将调用移出主线程,或者,如果您拥有该 API,请尝试优化该调用。 |
许多连续的 Binder 调用 | 主线程执行许多连续的同步 Binder 调用。 | 不要在紧密循环中执行 Binder 调用。 |
阻塞 I/O | 主线程执行阻塞式 I/O 调用,例如数据库或网络访问。 | 将所有阻塞式 I/O 移出主线程。 |
锁竞争 | 主线程被阻塞,等待获取锁。 | 减少主线程与其他线程之间的锁竞争。优化其他线程中的慢速代码。 |
昂贵的帧 | 在单个帧中渲染过多内容,导致严重的卡顿。 | 减少渲染帧的工作量。不要使用 n2 算法。对于滚动或分页等操作,请使用高效的组件,例如 Jetpack Paging 库。 |
被其他组件阻塞 | 另一个组件(例如广播接收器)正在运行并阻塞主线程。 | 尽可能将非 UI 工作移出主线程。在不同的线程上运行广播接收器。 |
GPU 挂起 | GPU 挂起是导致渲染被阻塞并因此导致输入分发 ANR 的系统或硬件问题。 | 不幸的是,应用端通常无法修复。如果可能,请联系硬件团队进行排查。 |
如何调试
通过查看 Google Play Console 或 Firebase Crashlytics 中的 ANR 集群签名来开始调试。集群通常包含被怀疑导致 ANR 的顶部帧。
以下流程图显示了如何确定输入超时分发 ANR 的原因。

Play Vitals 可以检测并帮助调试一些常见的 ANR 原因。例如,如果 Vitals 检测到 ANR 是由于锁竞争引起的,它可以在 ANR 洞察部分总结问题和建议的修复方法。

无焦点窗口
虽然触摸等事件会根据命中测试直接发送到相关窗口,但按键等事件需要一个目标。此目标被称为焦点窗口。每个显示器只有一个焦点窗口,通常是用户当前正在交互的窗口。如果找不到焦点窗口,输入会引发无焦点窗口 ANR。无焦点窗口 ANR 是一种输入分发 ANR。
默认超时时间:5 秒。
常见原因
无焦点窗口 ANR 通常由以下任一问题引起:
- 应用正在执行过多工作,并且绘制第一帧的速度过慢。
- 主窗口不可聚焦。如果窗口被标记为
FLAG_NOT_FOCUSABLE
,则用户无法向其发送按键或按钮事件。
Kotlin
override fun onCreate(savedInstanceState: Bundle) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) window.addFlags(WindowManager.LayoutParams.FLAG_FLAG_NOT_FOCUSABLE) }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); }
广播接收器超时
当广播接收器未能及时处理广播时,会发生广播接收器 ANR。对于同步接收器或未调用 goAsync()
的接收器,超时意味着 onReceive()
未能及时完成。对于异步接收器或调用 goAsync()
的接收器,超时意味着未能及时调用 PendingResult.finish()
。
广播接收器 ANR 通常发生在以下线程中:
- 主线程,如果问题是应用启动缓慢。
- 运行广播接收器的线程,如果问题是
onReceive()
代码缓慢。 - 广播工作线程,如果问题是
goAsync()
广播代码缓慢。
为避免广播接收器 ANR,请遵循以下最佳实践:
- 确保应用启动速度快,因为如果应用是为处理广播而启动的,其启动时间会计入 ANR 超时。
- 如果使用
goAsync()
,请确保快速调用PendingResult.finish()
。这与同步广播接收器受到相同的 ANR 超时限制。 - 如果使用
goAsync()
,请确保工作线程不与其他长时间运行或阻塞操作共享。 - 考虑使用
registerReceiver()
在非主线程中运行广播接收器,以避免阻塞主线程中运行的 UI 代码。
超时时间
广播接收超时时间取决于是否设置了前台 Intent 标志以及平台版本。
Intent 类型 | Android 13 及更低版本 | Android 14 及更高版本 |
---|---|---|
前台优先级 Intent (已设置 |
10 秒 |
10-20 秒,取决于进程是否 CPU 饥饿 |
后台优先级 Intent (未设置 |
60 秒 |
60-120 秒,取决于进程是否 CPU 饥饿 |
要判断是否设置了 FLAG_RECEIVER_FOREGROUND
标志,请在 ANR 主题中查找“flg=”,并检查是否存在 0x10000000
。如果设置了此位,则表示 intent 已设置 FLAG_RECEIVER_FOREGROUND
,因此超时时间更短。
短广播超时(10-20 秒)的 ANR 主题示例
Broadcast of Intent { act=android.inent.action.SCREEN_ON flg=0x50200010 }
长广播超时(60-120 秒)的 ANR 主题示例
Broadcast of Intent { act=android.intent.action.TIME_SET flg=0x25200010 }
广播时间如何测量
广播持续时间测量从广播从 system_server
分发到应用时开始,并在应用完成处理广播时结束。如果应用进程尚未运行,它还需要在 ANR 超时时间内进行冷启动。因此,应用启动缓慢可能导致广播接收器 ANR。
下图说明了广播接收器 ANR 时间线与某些应用进程的对应关系。

ANR 超时测量在接收器完成处理广播时结束:具体何时发生取决于它是同步接收器还是异步接收器。
- 对于同步接收器,测量在
onReceive()
返回时停止。 - 对于异步接收器,测量在
PendingResult.finish()
被调用时停止。

常见原因
以下是广播接收器 ANR 的一些常见原因和建议的修复方法。
原因 | 适用于 | 发生情况 | 建议修复 |
---|---|---|---|
应用启动缓慢 | 所有接收器 | 应用冷启动时间过长。 | 优化慢速应用启动。 |
onReceive() 未调度 | 所有接收器 | 广播接收器线程忙于其他工作,无法启动 onReceive() 方法。 | 不要在接收器线程上执行长时间运行的任务(或将接收器移至专用线程)。 |
缓慢的 onReceive() | 所有接收器,但主要是同步接收器 | onReceive() 方法已启动,但被阻塞或运行缓慢,因此未能及时完成。 | 优化慢速接收器代码。 |
异步接收器任务未调度 | goAsync() 接收器 | onReceive() 方法试图在被阻塞的工作线程池上执行工作,因此该工作从未开始。 |
优化缓慢或阻塞的调用,或者为广播工作线程和其它长时间运行的任务使用不同的线程。 |
工作线程缓慢或阻塞 | goAsync() 接收器 |
在处理广播时,工作线程池中的某个地方存在阻塞或缓慢的操作。因此,未能及时调用 PendingResult.finish 。 | 优化缓慢的 async 接收器代码。 |
忘记调用 PendingResult.finish |
goAsync() 接收器 |
代码路径中缺少对 finish() 的调用。 |
确保始终调用 finish() 。 |
如何调试
根据集群签名和 ANR 报告,您可以找到接收器运行的线程,然后找到缺少或运行缓慢的特定代码。
以下流程图显示了如何确定广播接收器 ANR 的原因。

查找接收器代码
Google Play Console 在 ANR 签名中显示接收器类和广播 Intent。查找以下内容:
cmp=<接收器类>
act=<广播_intent>
以下是广播接收器 ANR 签名示例:
com.example.app.MyClass.myMethod
Broadcast of Intent { act=android.accounts.LOGIN_ACCOUNTS_CHANGED
cmp=com.example.app/com.example.app.MyAccountReceiver }
查找运行 onReceive() 方法的线程
如果您使用 Context.registerReceiver
指定自定义处理程序,则它是运行此处理程序的线程。否则,它是主线程。
示例:异步接收器任务未调度
本节将逐步介绍如何调试广播接收器 ANR 的示例。
假设 ANR 签名如下所示:
com.example.app.MyClass.myMethod
Broadcast of Intent {
act=android.accounts.LOG_ACCOUNTS_CHANGED cmp=com.example.app/com.example.app.MyReceiver }
根据签名,广播 intent 似乎是 android.accounts.LOG_ACCOUNTS_CHANGED
,接收器类是 com.example.app.MyReceiver
。
从接收器代码中,您可以确定线程池“BG Thread [0,1,2,3]”负责处理此广播的主要工作。查看堆栈转储,您可以看到所有四个后台 (BG) 线程具有相同的模式:它们运行一个阻塞调用 getDataSync
。由于所有 BG 线程都处于忙碌状态,广播无法及时处理,从而导致了 ANR。
BG Thread #0 (tid=26) Waiting
at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture:563)
at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture:68)
at com.example.app.getDataSync(<MyClass>:152)
...
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:644)
at com.google.android.libraries.concurrent.AndroidExecutorsModule.lambda$withStrictMode$5(AndroidExecutorsModule:451)
at com.google.android.libraries.concurrent.AndroidExecutorsModule$$ExternalSyntheticLambda8.run(AndroidExecutorsModule:1)
at java.lang.Thread.run(Thread.java:1012)
at com.google.android.libraries.concurrent.ManagedPriorityThread.run(ManagedPriorityThread:34)
有几种方法可以解决此问题:
- 找出
getDataSync
缓慢的原因并进行优化。 - 不要在所有四个 BG 线程上运行
getDataSync
。 - 更普遍地说,请确保 BG 线程池不会因长时间运行的操作而饱和。
- 为
goAsync
工作任务使用专用线程池。 - 使用无界线程池而不是有界 BG 线程池
示例:应用启动缓慢
应用启动缓慢可能导致多种类型的 ANR,尤其是广播接收器和执行服务 ANR。如果您在主线程堆栈中看到 ActivityThread.handleBindApplication
,则 ANR 的原因很可能是应用启动缓慢。
执行服务超时
执行服务 ANR 发生在应用的主线程未能及时启动服务时。具体来说,服务未能在超时时间内完成执行 onCreate()
和 onStartCommand()
或 onBind()
。
默认超时时间:前台服务 20 秒;后台服务 200 秒。ANR 超时时间包括应用冷启动(如果需要)以及对 onCreate()
、onBind()
或 onStartCommand()
的调用。
为避免执行服务 ANR,请遵循以下一般最佳实践:
- 确保应用启动速度快,因为如果应用是为运行服务组件而启动的,其启动时间会计入 ANR 超时。
- 确保服务的
onCreate()
、onStartCommand()
和onBind()
方法快速执行。 - 避免在主线程上运行来自其他组件的任何缓慢或阻塞操作;这些操作会阻止服务快速启动。
常见原因
下表列出了执行服务 ANR 的常见原因和建议的修复方法。
原因 | 发生情况 | 建议修复 |
---|---|---|
应用启动缓慢 | 应用冷启动时间过长。 | 优化慢速应用启动。 |
缓慢的 onCreate() 、onStartCommand () 或 onBind() |
服务组件的 onCreate() 、onStartCommand () 或 onBind() 方法在主线程上执行时间过长。 |
优化慢速代码。尽可能将慢速操作移出关键路径。 |
未调度(主线程在 onStart() 之前被阻塞) |
应用的主线程在服务启动前被另一个组件阻塞。 | 将其他组件的工作移出主线程。优化其他组件的阻塞代码。 |
如何调试
通过 Google Play Console 或 Firebase Crashlytics 中的集群签名和 ANR 报告,您通常可以根据主线程正在执行的操作来确定 ANR 的原因。
以下流程图描述了如何调试执行服务 ANR。

如果您已确定执行服务 ANR 是可操作的,请按照以下步骤帮助解决问题:
在 ANR 签名中查找服务组件类。在 Google Play Console 中,服务组件类显示在 ANR 签名中。在以下 ANR 详细信息示例中,它是
com.example.app/MyService
。com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly Executing service com.example.app/com.example.app.MyService
通过检查主线程中的以下重要函数调用,确定缓慢或阻塞操作是应用启动、服务组件的一部分,还是在其他位置。
主线程堆栈中的函数调用 含义 android.app.ActivityThread.handleBindApplication
应用正在启动,因此 ANR 是由应用启动缓慢引起的。 <ServiceClass>.onCreate()
[...]
android.app.ActivityThread.handleCreateService
服务正在创建,因此 ANR 很可能是由缓慢的 onCreate()
代码引起的。<ServiceClass>.onBind()
[...]
android.app.ActivityThread.handleBindService
服务正在绑定,因此 ANR 很可能是由缓慢的 onBind()
代码引起的。<ServiceClass>.onStartCommand()
[...]
android.app.ActivityThread.handleServiceArgs
服务正在启动,因此 ANR 很可能是由缓慢的 onStartCommand()
代码引起的。例如,如果
MyService
类中的onStartCommand()
方法很慢,则主线程将如下所示:at com.example.app.MyService.onStartCommand(FooService.java:25) at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:4820) at android.app.ActivityThread.-$$Nest$mhandleServiceArgs(unavailable:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2289) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loopOnce(Looper.java:205) at android.os.Looper.loop(Looper.java:294) at android.app.ActivityThread.main(ActivityThread.java:8176) at java.lang.reflect.Method.invoke(Native method:0)
如果您看不到任何重要的函数调用,还有其他几种可能性:
- 服务正在运行或关闭,这意味着堆栈获取得太晚。在这种情况下,您可以将 ANR 忽略为误报。
- 另一个应用组件正在运行,例如广播接收器。在这种情况下,主线程很可能被此组件阻塞,从而阻止服务启动。
如果您确实看到一个关键函数调用并能大致确定 ANR 发生的位置,请检查主线程堆栈的其余部分,以找到缓慢的操作并对其进行优化或将其移出关键路径。
有关服务的更多信息,请参阅以下页面:
内容提供器无响应
内容提供器 ANR 发生在远程内容提供器响应查询的时间超过超时期限并被终止时。
默认超时时间:由内容提供器使用 ContentProviderClient.setDetectNotResponding
指定。ANR 超时时间包括远程内容提供器查询运行的总时间,其中包括冷启动远程应用(如果它尚未运行)所需的时间。
为避免内容提供器 ANR,请遵循以下最佳实践:
- 确保应用启动速度快,因为如果应用是为运行内容提供器而启动的,其启动时间会计入 ANR 超时。
- 确保内容提供器查询速度快。
- 不要执行大量可能阻塞应用所有 Binder 线程的并发阻塞 Binder 调用。
常见原因
下表列出了内容提供器 ANR 的常见原因和建议的修复方法。
原因 | 发生情况 | 征兆 | 建议修复 |
---|---|---|---|
内容提供器查询缓慢 | 内容提供器执行时间过长或被阻塞。 | android.content.ContentProvider$Transport.query 帧位于 Binder 线程中。 |
优化内容提供器查询。找出是什么阻塞了 Binder 线程。 |
应用启动缓慢 | 内容提供器应用启动时间过长。 | ActivityThread.handleBindApplication 帧位于主线程中。 |
优化应用启动。 |
Binder 线程耗尽——所有 Binder 线程都忙碌 | 所有 Binder 线程都忙于处理其他同步请求,因此内容提供器 Binder 调用无法运行。 | 应用未启动,所有 Binder 线程都忙碌,并且内容提供器未运行。 | 减轻 Binder 线程上的负载。也就是说,减少同步传出 Binder 调用,或在处理传入调用时减少工作量。 |
如何调试
要使用 Google Play Console 或 Firebase Crashlytics 中的集群签名和 ANR 报告调试内容提供器 ANR,请查看主线程和 Binder 线程正在执行的操作。
以下流程图描述了如何调试内容提供器 ANR:

以下代码片段显示了 Binder 线程因内容提供器查询缓慢而被阻塞时的样子。在这种情况下,内容提供器查询在打开数据库时正在等待锁。
binder:11300_2 (tid=13) Blocked
Waiting for osm (0x01ab5df9) held by at com.google.common.base.Suppliers$NonSerializableMemoizingSupplier.get(Suppliers:182)
at com.example.app.MyClass.blockingGetOpenDatabase(FooClass:171)
[...]
at com.example.app.MyContentProvider.query(MyContentProvider.java:915)
at android.content.ContentProvider$Transport.query(ContentProvider.java:292)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:107)
at android.os.Binder.execTransactInternal(Binder.java:1339)
at android.os.Binder.execTransact(Binder.java:1275)
以下代码片段显示了主线程因应用启动缓慢而被阻塞时的样子。在这种情况下,应用启动缓慢是由于 dagger 初始化期间的锁竞争。
main (tid=1) Blocked
[...]
at dagger.internal.DoubleCheck.get(DoubleCheck:51)
- locked 0x0e33cd2c (a qsn)at dagger.internal.SetFactory.get(SetFactory:126)
at com.myapp.Bar_Factory.get(Bar_Factory:38)
[...]
at com.example.app.MyApplication.onCreate(DocsApplication:203)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1316)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6991)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(unavailable:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2235)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8170)
at java.lang.reflect.Method.invoke(Native method:0)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
作业响应缓慢
作业响应缓慢 ANR 发生在应用响应 JobService.onStartJob()
或 JobService.onStopJob()
时间过长,或使用 JobService.setNotification()
提供通知时间过长时。这表明应用的主线程被阻塞,正在执行其他操作。
如果是 JobService.onStartJob()
或 JobService.onStopJob()
的问题,请检查主线程正在发生什么。如果是 JobService.setNotification()
的问题,请确保尽快调用它。在提供通知之前不要做大量工作。
神秘 ANR
有时不清楚 ANR 发生的原因,或者集群签名和 ANR 报告中没有足够的信息来调试它。在这种情况下,您仍然可以采取一些步骤来确定 ANR 是否可操作。
消息队列空闲或 nativePollOnce
如果您在堆栈中看到 android.os.MessageQueue.nativePollOnce
帧,这通常表示可疑的无响应线程实际上处于空闲状态,正在等待 looper 消息。在 Google Play Console 中,ANR 详细信息如下所示:
Native method - android.os.MessageQueue.nativePollOnce
Executing service com.example.app/com.example.app.MyService
例如,如果主线程空闲,堆栈如下所示:
"main" tid=1 NativeMain threadIdle
#00 pc 0x00000000000d8b38 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8)
#01 pc 0x0000000000019d88 /system/lib64/libutils.so (android::Looper::pollInner(int)+184)
#02 pc 0x0000000000019c68 /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+112)
#03 pc 0x000000000011409c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce (Native method)
at android.os.MessageQueue.next (MessageQueue.java:339) at android.os.Looper.loop (Looper.java:208)
at android.app.ActivityThread.main (ActivityThread.java:8192)
at java.lang.reflect.Method.invoke (Native method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:626)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1015)
可疑的无响应线程可能空闲的原因有几个:
- 延迟堆栈转储。在 ANR 触发和堆栈转储之间的短时间内,线程已恢复。Android 13 上 Pixel 设备的延迟约为 100 毫秒,但可能超过 1 秒。Android 14 上 Pixel 设备的延迟通常低于 10 毫秒。
- 线程归因错误。用于构建 ANR 签名的线程并非导致 ANR 的实际无响应线程。在这种情况下,请尝试确定 ANR 是否属于以下类型之一:
- 系统范围问题。由于系统负载过重或系统服务器中的问题,进程未被调度。
无堆栈帧
某些 ANR 报告不包含带 ANR 的堆栈,这意味着在生成 ANR 报告时堆栈转储失败。堆栈帧缺失的原因可能有以下几点:
- 获取堆栈时间过长并超时。
- 进程在堆栈获取之前已终止或被杀死。
[...]
--- CriticalEventLog ---
capacity: 20
timestamp_ms: 1666030897753
window_ms: 300000
libdebuggerd_client: failed to read status response from tombstoned: timeout reached?
----- Waiting Channels: pid 7068 at 2022-10-18 02:21:37.<US_SOCIAL_SECURITY_NUMBER>+0800 -----
[...]
没有堆栈帧的 ANR 无法从集群签名或 ANR 报告中进行操作。要进行调试,请查看应用的其它集群,因为如果问题足够大,通常会在其自己的集群中存在堆栈帧。另一个选择是查看 Perfetto 跟踪。
已知问题
在应用进程中设置计时器以在 ANR 触发之前完成广播处理可能无法正常工作,因为系统监视 ANR 的方式是异步的。