ANR を診断して修正する

Android アプリの UI スレッドが長時間ブロックされると、システムから「アプリケーション応答なし(ANR)」エラーが送信されます。このページでは、さまざまな種類の ANR の概要、診断方法、おすすめの修正方法について説明します。記載されているデフォルトのタイムアウト時間はすべて、AOSP と Google Pixel デバイスを対象としています。時間は OEM によって異なります。

ANR の原因を特定する際には、システムの問題とアプリの問題を切り分けることが重要となります。

システムが不適切な状態の場合、以下の問題によって ANR が発生する可能性があります。

  • システム サーバーの一時的な問題により、通常高速なバインダー呼び出しが遅くなります。
  • システム サーバーに問題がある場合やデバイスの負荷が高い場合、アプリのスレッドがスケジュールされません。

可能であれば、Perfetto トレースを使用してシステムの問題とアプリの問題を切り分けることをおすすめします。

  • Perfetto のスレッド ステータス トラッキング機能でアプリのメインスレッドが実行中または実行可能かどうかをチェックし、スケジュールされているかどうかを確認します。
  • system_server スレッドにロック競合などの問題がないかを確認します。
  • バインダー呼び出しが遅い場合、応答スレッド(存在する場合)を調べて、遅い理由を確認します。

入力ディスパッチのタイムアウト

入力ディスパッチ ANR は、アプリのメインスレッドが入力イベント(スワイプやキーの押下など)に時間内に応答していない場合に発生します。入力ディスパッチ タイムアウトが発生する際、アプリはフォアグラウンドにあり、ほぼ常にユーザーに表示されているため、発生しないようにすることが非常に重要です。

デフォルトのタイムアウト時間: 5 秒。

入力ディスパッチ ANR は通常、メインスレッドの問題が原因で発生します。メインスレッドがロックの取得を待ってブロックされた場合は、ロックを保持しているスレッドも関係している可能性があります。

入力ディスパッチ ANR が発生しないようにするには、次のおすすめの方法を参考にしてください。

  • メインスレッドではブロッキング オペレーションや長時間実行オペレーションを実行しないようにします。メインスレッドで誤って実行しているアクティビティを検出するために、StrictMode の使用を検討してください。
  • メインスレッドと他のスレッド間のロック競合を最小限に抑えます。
  • ブロードキャストの処理やサービスの実行など、メインスレッドでの UI 以外の処理は最小限に抑えます。

一般的な原因

入力ディスパッチ ANR の一般的な原因と推奨される修正方法は次のとおりです。

原因 現象 推奨される修正方法
バインダー呼び出しが遅い メインスレッドが長時間の同期バインダー呼び出しを行っている。 呼び出しをメインスレッドから移すか、呼び出しを最適化してみる(API を所有している場合)。
多数の連続するバインダー呼び出し メインスレッドが多数の同期バインダー呼び出しを連続して行っている。 タイトなループでバインダー呼び出しを実行しない。
I/O のブロッキング メインスレッドがデータベースやネットワーク アクセスなどの I/O ブロッキング呼び出しを行っている。 I/O のブロッキングをすべてメインスレッドから移す。
ロックの競合 メインスレッドがロックの取得を待ってブロックされている。 メインスレッドと他のスレッド間のロック競合を削減する。他のスレッドの遅いコードを最適化する。
コストのかかるフレーム 1 つのフレームでのレンダリングが多すぎて、重度のジャンクが発生している。 フレームのレンダリング処理を減らす。n2アルゴリズムは使用しない。スクロールやページングなどには効率的なコンポーネント(Jetpack ページング ライブラリなど)を使用する。
他のコンポーネントによるブロック ブロードキャスト レシーバなどの別のコンポーネントが実行されており、メインスレッドをブロックしている。 UI 以外の処理は可能な限りメインスレッドから移す。ブロードキャスト レシーバは別のスレッドで実行する。
GPU のハング システムまたはハードウェアの問題である GPU のハングによってレンダリングがブロックされ、入力ディスパッチ ANR が発生する。 通常はアプリ側で修正することはできない。可能であれば、ハードウェア チームにトラブルシューティングを依頼する。

デバッグ方法

まず、Google Play Console または Firebase Crashlytics で ANR クラスタ シグネチャを確認します。通常、クラスタには ANR の発生原因と思われるトップフレームが含まれています。

次のフローチャートは、入力タイムアウト ディスパッチ ANR の原因を特定する方法を示しています。

図 1.入力ディスパッチ ANR のデバッグ方法

Play Vitals を使用すると、このような一般的な ANR の原因を検出してデバッグできます。たとえば、ロックの競合が原因で ANR が発生したことが Vitals で検出された場合、ANR の [分析情報] セクションに問題と推奨される修正方法の概要が表示されます。

図 2.Play Vitals の ANR 検出

フォーカスされているウィンドウなし

タップなどのイベントはヒットテストに基づいて関連するウィンドウに直接送信されますが、キーなどのイベントにはターゲットが必要です。このターゲットはフォーカスされているウィンドウと呼ばれます。ディスプレイごとにフォーカスされているウィンドウは 1 つのみで、通常はユーザーが現在操作しているウィンドウとなります。フォーカスされているウィンドウが見つからない場合、入力によって、フォーカスされているウィンドウなし 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 は、ブロードキャスト レシーバがブロードキャストを時間内に処理できなかった場合に発生します。同期レシーバまたは goAync() を呼び出さないレシーバの場合、タイムアウトは onReceive() が時間内に完了しなかったことを意味します。非同期レシーバまたは goAsync() を呼び出すレシーバの場合、タイムアウトは PendingResult.finish() が時間内に呼び出されなかったことを意味します。

ブロードキャスト レシーバ ANR は、通常次のスレッドで発生します。

  • メインスレッド(アプリの起動が遅いことが原因の場合)。
  • ブロードキャスト レシーバを実行しているスレッド(onReceive() コードの遅れが原因の場合)。
  • ブロードキャスト ワーカー スレッド(goAsync() ブロードキャスト コードの遅れが原因の場合)。

ブロードキャスト レシーバ ANR が発生しないようにするには、次のおすすめの方法を参考にしてください。

  • 速やかにアプリが起動するようにします。アプリを起動してブロードキャストの処理を開始する場合、起動時間は ANR タイムアウトにカウントされます。
  • goAsync() を使用する場合は、PendingResult.finish() が速やかに呼び出されるようにします。これには同期ブロードキャスト レシーバと同じ ANR タイムアウトが適用されます。
  • goAsync() を使用する場合は、ワーカー スレッドが他の長時間実行オペレーションやブロッキング オペレーションと共有されないようにします。
  • メインスレッドで実行されている UI コードがブロックされないように、registerReceiver() を使用してメインスレッド以外でブロードキャスト レシーバを実行することを検討してください。

タイムアウト時間

ブロードキャスト レシーバのタイムアウト時間は、フォアグラウンドのインテント フラグの設定有無と、プラットフォームのバージョンによって異なります。

インテントの種類 Android 13 以前 Android 14 以降

フォアグラウンド優先インテント

FLAG_RECEIVER_FOREGROUND 設定済み)

10 秒

10~20 秒(プロセスに必要な CPU が不足しているかどうかによって異なる)

バックグラウンド優先インテント

FLAG_RECEIVER_FOREGROUND 未設定)

60 秒

60~120 秒(プロセスに必要な CPU が不足しているかどうかによって異なる)

FLAG_RECEIVER_FOREGROUND フラグが設定されているかどうかを判別するには、ANR サブジェクトで「flg=」を検索し、0x10000000 が存在するかどうかを確認します。このビットが設定されている場合、インテントに 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 のタイムラインが特定のアプリプロセスと一致していることを示しています。

図 3.ブロードキャスト レシーバ ANR のタイムライン

ANR タイムアウトの測定は、レシーバがブロードキャストの処理を完了すると終了します。終了する正確なタイミングは、同期レシーバか非同期レシーバかによって異なります。

  • 同期レシーバの場合は、onReceive() が返されると終了します。
  • 非同期レシーバの場合は、PendingResult.finish() が呼び出されると終了します。
図 4.同期レシーバと非同期レシーバの ANR タイムアウト測定終了ポイント

一般的な原因

ブロードキャスト レシーバ ANR の一般的な原因と推奨される修正方法は次のとおりです。

原因 該当するレシーバ 現象 推奨される修正方法
アプリの起動が遅い すべてのレシーバ アプリのコールド スタートに時間がかかりすぎている。 アプリの起動スピードを最適化する。
onReceive() が未設定 すべてのレシーバ ブロードキャスト レシーバ スレッドが他の処理を行っているためビジー状態で、onReceive() メソッドを開始できない。 レシーバ スレッドで長時間実行タスクを実行しない(または、レシーバを専用スレッドに移す)。
onReceive() の遅延 すべてのレシーバ(ただし、主に同期レシーバ) onReceive() メソッドは開始されたものの、ブロックされたか低速であるため、時間内に完了しなかった。 レシーバコードの遅延を最適化する。
非同期レシーバタスクが未設定 goAsync() レシーバ onReceive() メソッドがブロックされたワーカー スレッド プールで処理を実行しようとしたため、処理が開始されなかった。 時間のかかる呼び出しやブロッキング呼び出しを最適化するか、ブロードキャスト ワーカーとその他の長時間実行タスクでスレッドを分ける。
ワーカーが遅い、またはブロックされている goAsync() レシーバ ブロードキャストの処理中に、ワーカー スレッド プールのどこかでブロッキング オペレーションまたは低速なオペレーションが発生した。そのため、PendingResult.finish が時間内に呼び出されなかった。 async レシーバコードの遅延を最適化する。
PendingResult.finish の呼び出し漏れ goAsync() レシーバ finish() の呼び出しがコードパスから漏れている。 finish() が必ず呼び出されるようにする。

デバッグ方法

クラスタ シグネチャと ANR レポートに基づいて、レシーバが実行されているスレッドと、漏れているコードや時間がかかっているコードを特定します。

次のフローチャートは、ブロードキャスト レシーバ ANR の原因を特定する方法を示しています。

図 5.ブロードキャスト レシーバ ANR のデバッグ方法

レシーバコードを確認する

Google Play Console には、ANR シグネチャ内のレシーバクラスとブロードキャスト インテントが表示されます。次のものを確認します。

  • cmp=<receiver class>
  • act=<broadcast_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 }

シグネチャから、ブロードキャスト インテントは android.accounts.LOG_ACCOUNTS_CHANGED で、レシーバクラスは com.example.app.MyReceiver です。

レシーバコードから、このブロードキャストを処理する主な作業は、スレッドプールの「BG Thread [0,1,2,3]」が実行していることがわかります。スタックダンプを見ると、4 つのバックグラウンド(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)

There are several approaches to fix the issue:

  • Find out why getDataSync is slow and optimize.
  • Don't run getDataSync on all four BG threads.
  • More generally, ensure that the BG thread pool isn't saturated with long-running operations.
  • Use a dedicated thread pool for goAsync worker tasks.
  • Use an unbounded thread pool instead of the bounded BG thread pool

Example: slow app startup

A slow app startup can cause several types of ANRs, especially broadcast receiver and execute service ANRs. The cause of an ANR is likely slow app startup if you see ActivityThread.handleBindApplication in the main thread stacks.

Execute service timeout

An execute service ANR happens when the app's main thread doesn't start a service in time. Specifically, a service doesn't finish executing onCreate() and onStartCommand() or onBind() within the timeout period.

Default timeout period: 20 seconds for foreground service; 200 seconds for background service. The ANR timeout period includes the app cold start, if necessary, and calls to onCreate(), onBind(), or onStartCommand().

To avoid execute service ANRs, follow these general best practices:

  • Make sure that app startup is fast, since it's counted in the ANR timeout if the app is started to run the service component.
  • Make sure that the service's onCreate(), onStartCommand(), and onBind() methods are fast.
  • Avoid running any slow or blocking operations on the main thread from other components; these operations can prevent a service from starting quickly.

Common causes

The following table lists common causes of execute service ANRs and suggested fixes.

Cause What Suggested fix
Slow app startup The app takes too long to perform a cold start. Optimize slow app start.
Slow onCreate(), onStartCommand(), or onBind() The service component's onCreate(), onStartCommand(), or onBind() method takes too long to execute on the main thread. Optimize slow code. Move slow operations off the critical path where possible.
Not scheduled (main thread blocked before onStart()) The app's main thread is blocked by another component before the service can be started. Move other component's work off the main thread. Optimize other component's blocking code.

How to debug

From the cluster signature and ANR report in Google Play Console or Firebase Crashlytics, you can often determine the cause of the ANR based on what the main thread is doing.

The following flow chart describes how to debug an execute service ANR.

Figure 6. How to debug an execute service ANR.

If you've determined that the execute service ANR is actionable, follow these steps to help resolve the issue:

  1. Find the service component class in the ANR signature. In Google Play Console, the service component class is shown in the ANR signature. In the following example ANR details, it's com.example.app/MyService.

    com.google.common.util.concurrent.Uninterruptibles.awaitUninterruptibly
    Executing service com.example.app/com.example.app.MyService
    
  2. Determine whether the slow or block operation is part of app startup, the service component, or elsewhere by checking for the following important function call(s) in the main threads.

    Function call(s) in main thread stacks What it means
    android.app.ActivityThread.handleBindApplication App was starting up, so the ANR was caused by slow app start.

    <ServiceClass>.onCreate()

    [...]

    android.app.ActivityThread.handleCreateService

    Service was being created, so the ANR was likely caused by slow onCreate() code.

    <ServiceClass>.onBind()

    [...]

    android.app.ActivityThread.handleBindService

    Service was being bound, so the ANR was likely caused by slow onBind() code.

    <ServiceClass>.onStartCommand()

    [...]

    android.app.ActivityThread.handleServiceArgs

    Service was being started, so the ANR was likely caused by slow onStartCommand() code.

    For example, if the onStartCommand() method in the MyService class is slow, the main threads will look like this:

    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 を無視できます。
    • ブロードキャスト レシーバなど、別のアプリ コンポーネントが実行されている。この場合、メインスレッドがこのコンポーネントでブロックされ、サービスを開始できない可能性があります。
  3. 重要な関数呼び出しを確認し、ANR がどこで発生しているかを特定できる場合は、残りのメインスレッド スタックを調べ、時間のかかるオペレーションを見つけて最適化するか、クリティカル パスから外します。

  4. サービスの詳細については、次のページをご覧ください。

    コンテンツ プロバイダの応答なし

    コンテンツ プロバイダ ANR は、リモート コンテンツ プロバイダがタイムアウト時間内にクエリに応答できず、強制終了されると発生します。

    デフォルトのタイムアウト時間: ContentProviderClient.setDetectNotResponding を使用してコンテンツ プロバイダが指定します。ANR タイムアウト時間には、リモート コンテンツ プロバイダ クエリの総実行時間が含まれます。その中には、リモートアプリがまだ実行されていない場合のコールド スタートも含まれます。

    コンテンツ プロバイダ ANR が発生しないようにするには、次のおすすめの方法を参考にしてください。

    • 速やかにアプリが起動するようにします。アプリを起動してコンテンツ プロバイダを実行する場合、起動時間は ANR タイムアウトにカウントされます。
    • コンテンツ プロバイダのクエリに時間がかからないようにします。
    • アプリのすべてのバインダー スレッドをブロックする可能性があるため、同時にバインダー ブロッキング呼び出しを大量に実行しないでください。

    一般的な原因

    次の表は、コンテンツ プロバイダ ANR の一般的な原因と推奨される修正方法を示しています。

    原因 現象 シグナル 推奨される修正方法
    コンテンツ プロバイダのクエリが遅い コンテンツ プロバイダの実行に時間がかかりすぎているか、コンテンツ プロバイダがブロックされている。 android.content.ContentProvider$Transport.query フレームがバインダー スレッド内にある。 コンテンツ プロバイダのクエリを最適化する。バインダー スレッドをブロックしているものを特定する。
    アプリの起動が遅い コンテンツ プロバイダのアプリの起動に時間がかかりすぎている。 ActivityThread.handleBindApplication フレームがメインスレッドにある。 アプリの起動を最適化する。
    バインダー スレッドの枯渇 - すべてのバインダー スレッドがビジー状態 すべてのバインダー スレッドが他の同期リクエストを処理していてビジー状態のため、コンテンツ プロバイダのバインダー呼び出しを実行できない。 アプリは起動しておらず、すべてのバインダー スレッドがビジー状態で、コンテンツ プロバイダが実行されていない。 バインダー スレッドの負荷を軽減する。同期バインダー呼び出しを減らすか、呼び出しに対応する際の処理量を減らす。

    デバッグ方法

    Google Play Console または Firebase Crashlytics のクラスタ シグネチャと ANR レポートを使用してコンテンツ プロバイダ ANR をデバッグするには、メインスレッドとバインダー スレッドが実行している内容を確認します。

    次のフローチャートは、コンテンツ プロバイダ ANR のデバッグ方法を示しています。

    図 7.コンテンツ プロバイダ ANR のデバッグ方法

    次のコード スニペットは、コンテンツ プロバイダのクエリが遅いためにブロックされたバインダー スレッドを表しています。この例では、コンテンツ プロバイダのクエリはデータベースを開くときにロックを待っています。

    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 をデバッグするための情報が不足している場合があります。そのような場合でも、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 の Google Pixel のレイテンシは約 100 ミリ秒ですが、1 秒を超えることもあります。Android 14 の Google 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 を非同期でモニタリングするため、正しく機能しない可能性があります。