本文档介绍了如何在 ANR 堆栈转储中识别无响应线程。无响应线程因 ANR 类型而异,如下表所示。
ANR 类型 | 无响应线程 |
---|---|
输入分发 | 主线程 |
输入分发无焦点窗口 | 主线程。此类型的 ANR 通常不是由阻塞线程引起的。 |
广播接收器(同步) | 正在运行 onReceive() 的线程。除非使用 Context.registerReceiver 在非主线程上指定自定义处理程序,否则这是主线程。 |
广播接收器(异步) | 检查代码以查看哪个线程或线程池负责在调用 goAsync 后处理广播的工作。 |
执行服务超时 | 主线程 |
前台服务启动 | 主线程 |
内容提供程序无响应 | 任一
|
对 onStartJob 或 onStopJob 没有响应 |
主线程 |
有时线程无响应是由于其他线程或进程中的根本原因导致的。线程可能由于等待以下内容而无响应
- 其他线程持有的锁。
- 对其他进程的缓慢 Binder 调用。
线程无响应的常见原因
以下是线程无响应的常见原因。
缓慢的 Binder 调用
尽管大多数 Binder 调用都很快速,但长尾部分可能非常缓慢。如果设备负载过重或 Binder 回复线程速度缓慢(例如,由于锁争用、大量传入的 Binder 调用或 硬件抽象层 (HAL) 超时),则更有可能发生这种情况。
您可以通过尽可能将同步 Binder 调用移至后台线程来解决此问题。如果必须在主线程上进行调用,请找出调用速度缓慢的原因。最好的方法是从 Perfetto 跟踪中获取信息。
在堆栈中查找 BinderProxy.transactNative
或 Binderproxy.transact
。这意味着正在进行 Binder 调用。在接下来的两行中,您可以看到调用的 Binder API。在以下示例中,调用的是 IAccessibilityManager.addClient
。
main tid=123
...
android.os.BinderProxy.transactNative (Native method)
android.os.BinderProxy.transact (BinderProxy.java:568)
android.view.accessibility.IAccessibilityManager$Stub$Proxy.addClient (IAccessibilityManager.java:599)
...
许多连续的 Binder 调用
在紧密循环中执行许多连续的 Binder 调用可能会阻塞线程很长时间。
阻塞式 I/O
切勿在主线程上执行阻塞式 I/O。这是一种反模式。
锁争用
如果线程在获取锁时被阻塞,则可能导致 ANR。
以下示例显示了主线程在尝试获取锁时被阻塞的情况
main (tid=1) Blocked
Waiting for com.example.android.apps.foo.BarCache (0x07d657b7) held by
ptz-rcs-28-EDITOR_REMOTE_VIDEO_DOWNLOAD
[...]
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:5412)
[...]
阻塞线程正在发出 HTTP 请求以下载视频
ptz-rcs-28-EDITOR_REMOTE_VIDEO_DOWNLOAD (tid=110) Waiting
at jdk.internal.misc.Unsafe.park(Native method:0)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:211)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:715)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(AbstractQueuedSynchronizer.java:1047)
at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:230)
at com.example.android.apps.foo.HttpRequest.execute(HttpRequest:136)
at com.example.android.apps.foo$Task$VideoLoadTask.downloadVideoToFile(RequestExecutor:711)
[...]
昂贵的帧
在一帧中渲染过多内容会导致主线程在帧持续时间内无响应,例如以下情况
- 渲染许多不必要的屏幕外项目。
- 使用低效算法(例如
O(n^2)
)渲染许多 UI 元素。
被其他组件阻塞
如果另一个组件(例如广播接收器)阻塞主线程超过五秒,则可能导致输入调度 ANR 和严重的卡顿。
避免在应用组件的主线程上执行任何繁重的工作。尽可能在其他线程上运行广播接收器。