本文档介绍了如何在 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 和严重的卡顿。
避免在应用组件的主线程上执行任何繁重的工作。尽可能在其他线程上运行广播接收器。