応答しないスレッドを見つける

このドキュメントでは、ANR スタックダンプで応答しないスレッドを特定する方法について説明します。以下の表に示すとおり、応答しないスレッドは ANR の種類によって異なります。

ANR の種類 応答しないスレッド
入力ディスパッチ メインスレッド
フォーカスがあるウィンドウが入力ディスパッチにない メインスレッド。通常、この種の ANR はブロックされたスレッドでは発生しません。
ブロードキャスト レシーバ(同期) onReceive() を実行しているスレッド。 非メインスレッドのカスタム ハンドラが Context.registerReceiver を使用して指定されていない限り、メインスレッドです。
ブロードキャスト レシーバ(非同期) コードで、goAsync の呼び出し後にどのスレッドまたはスレッドプールがブロードキャストを処理する作業を行っているかを確認します。
実行中のサービスのタイムアウト メインスレッド
フォアグラウンド サービスの開始 メインスレッド
コンテンツ プロバイダの応答なし 以下のいずれか:
  • ANR の原因がコンテンツ プロバイダのクエリが遅いことにある場合は、バインダー スレッド。
  • ANR の原因がアプリの長い起動にある場合は、メインスレッド。
onStartJob または onStopJob の応答がない メインスレッド

別のスレッドまたはプロセスに根本原因があるために、スレッドが応答しないことがあります。スレッドが応答しなくなる可能性がある待機は以下のとおりです。

  • 別のスレッドで保持されているロックの待機。
  • 別のプロセスに対する遅いバインダー呼び出しの待機。

応答しないスレッドの一般的な原因

応答しないスレッドの一般的な原因は以下のとおりです。

バインダー呼び出しが遅い

ほとんどのバインダー呼び出しは高速ですが、ロングテールはかなり遅くなることがあります。これは、デバイスが読み込まれる場合や、ロックの競合、受信したバインダー呼び出しの多さ、Hardware Abstraction Layer(HAL)のタイムアウトなどにより、バインダー返信スレッドが遅い場合に発生する可能性が高くなります。

この問題は、可能な限り、同期のバインダー呼び出しをバックグラウンド スレッドに移動することで解決できます。メインスレッドで呼び出しを行う必要がある場合は、呼び出しが遅い理由を突き止めます。このためには、Perfetto トレースを使用することをおすすめします。

スタックで BinderProxy.transactNative または Binderproxy.transact を探します。 これは、バインダー呼び出しが行われていることを意味します。これらの 2 つの行の後で、呼び出されるバインダー 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)
[...]

コストのかかるフレーム

以下のように、1 つのフレーム内にレンダリングするものが多すぎると、そのフレームの最中にメインスレッドが応答しなくなることがあります。

  • 数多くの不要な画面外アイテムをレンダリングする。
  • 多くの UI 要素をレンダリングする際に、O(n^2) などの非効率的アルゴリズムを使用している。

他のコンポーネントによるブロック

ブロードキャスト レシーバなどの別のコンポーネントによってメインスレッドが 5 秒以上ブロックされると、入力ディスパッチの ANR や重大なジャンクが発生することがあります。

アプリ コンポーネントのメインスレッドで負荷の大きい処理を行わないでください。ブロードキャスト レシーバは、可能な限り別のスレッドで実行してください。