找出無回應的會話串

本文件說明如何在 ANR 堆疊傾印中找出無回應的執行緒。無回應的執行緒會因 ANR 類型而異,如下表所示。

ANR 類型 無回應的執行緒
輸入調度 主執行緒
輸入調度無聚焦視窗 主執行緒。這類 ANR 通常不是因執行緒遭阻斷而造成。
廣播接收器 (同步) 執行 onReceive() 的執行緒。除非使用 Context.registerReceiver 指定非主執行緒上的自訂處理常式,否則這會是主執行緒。
廣播接收器 (非同步) 請查看程式碼,瞭解在呼叫 goAsync 後,是由哪個執行緒或執行緒集區負責處理廣播。
執行服務逾時 主執行緒
前景服務啟動 主執行緒
內容供應器沒有回應 以下兩者之一:
  • 如果 ANR 發生原因是內容供應器查詢速度緩慢,則為繫結器執行緒。
  • 如果 ANR 發生原因是應用程式啟動時間過長,則為主執行緒。
onStartJobonStopJob 無回應 主執行緒

有時候,執行緒無回應是因為其他執行緒/程序中的根本問題。執行緒可能因等待以下項目而沒有回應:

  • 由其他執行緒保留的鎖定。
  • 對不同程序發出的緩慢繫結器呼叫。

導致執行緒無回應的常見原因

以下是導致執行緒無回應的常見原因。

繫結器呼叫速度緩慢

雖然大多數繫結器呼叫的速度都很快,但長尾呼叫可能非常慢。如果在載入裝置或繫結器回覆執行緒過慢時,例如因鎖定爭用、傳入許多繫結器呼叫,或硬體抽象層 (HAL) 逾時,就較可能出現這種情況。

如要解決這個問題,請盡可能將同步繫結器呼叫移至背景執行緒。如果呼叫必須在主執行緒上執行,請找出呼叫速度緩慢的原因。最佳做法是使用 Perfetto 追蹤記錄。

建議您在堆疊中尋找 BinderProxy.transactNativeBinderproxy.transact,這表示繫結器呼叫正在執行。您可以依據這兩行程式碼,瞭解系統呼叫的繫結器 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)
...

連續多次發出繫結器呼叫

在緊密迴圈中連續多次執行繫結器呼叫,可能會長時間阻斷執行緒。

阻斷式 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)
[...]

高成本影格

如果在單一影格中轉譯過多內容,可能會導致主執行緒在影格持續期間沒有回應,例如:

  • 轉譯許多不必要的畫面外項目。
  • 在轉譯許多 UI 元素時使用效率不佳的演算法,例如 O(n^2)

遭其他元件阻斷

如果廣播接收器等其他元件阻斷主執行緒的時間超過五秒,就可能導致輸入調度 ANR 和嚴重卡頓。

請避免在應用程式元件的主執行緒上執行大量作業,應盡可能在不同執行緒中執行廣播接收器。