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 の原因を特定する方法を示しています。
Play Vitals を使用すると、このような一般的な ANR の原因を検出してデバッグできます。たとえば、ロックの競合が原因で ANR が発生したことが 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 は、ブロードキャスト レシーバがブロードキャストを時間内に処理できなかった場合に発生します。同期レシーバまたは goAsync() を呼び出さないレシーバの場合、タイムアウトは onReceive() が時間内に完了しなかったことを意味します。非同期レシーバまたは goAsync() を呼び出すレシーバの場合、タイムアウトは PendingResult.finish() が時間内に呼び出されなかったことを意味します。
ブロードキャスト レシーバ ANR は、通常次のスレッドで発生します。
- メインスレッド(アプリの起動が遅いことが原因の場合)。
- ブロードキャスト レシーバを実行しているスレッド(
onReceive()コードの遅れが原因の場合)。 - ブロードキャスト ワーカー スレッド(
goAsync()ブロードキャスト コードの遅れが原因の場合)。
ブロードキャスト レシーバ ANR が発生しないようにするには、次のおすすめの方法を参考にしてください。
- 速やかにアプリが起動するようにします。アプリを起動してブロードキャストの処理を開始する場合、起動時間は ANR タイムアウトにカウントされます。
goAsync()を使用する場合は、PendingResult.finish()が速やかに呼び出されるようにします。これには同期ブロードキャスト レシーバと同じ ANR タイムアウトが適用されます。goAsync()を使用する場合は、ワーカー スレッドが他の長時間実行オペレーションやブロッキング オペレーションと共有されないようにします。- メインスレッドで実行されている UI コードがブロックされないように、
registerReceiver()を使用してメインスレッド以外でブロードキャスト レシーバを実行することを検討してください。
タイムアウト時間
ブロードキャスト レシーバのタイムアウト時間は、フォアグラウンドのインテント フラグの設定有無と、プラットフォームのバージョンによって異なります。
| インテントの種類 | Android 13 以前 | Android 14 以降 |
|---|---|---|
フォアグラウンド優先インテント ( |
10 秒 |
10~20 秒(プロセスに必要な CPU が不足しているかどうかによって異なる) |
バックグラウンド優先インテント ( |
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 のタイムラインが特定のアプリプロセスと一致していることを示しています。

ANR タイムアウトの測定は、レシーバがブロードキャストの処理を完了すると終了します。終了する正確なタイミングは、同期レシーバか非同期レシーバかによって異なります。
- 同期レシーバの場合は、
onReceive()が返されると終了します。 - 非同期レシーバの場合は、
PendingResult.finish()が呼び出されると終了します。

一般的な原因
ブロードキャスト レシーバ ANR の一般的な原因と推奨される修正方法は次のとおりです。
| 原因 | 該当するレシーバ | 現象 | 推奨される修正方法 |
|---|---|---|---|
| アプリの起動が遅い | すべてのレシーバ | アプリのコールド スタートに時間がかかりすぎている。 | アプリの起動スピードを最適化する。 |
onReceive() が未設定 | すべてのレシーバ | ブロードキャスト レシーバ スレッドが他の処理を行っているためビジー状態で、onReceive() メソッドを開始できない。 | レシーバ スレッドで長時間実行タスクを実行しない(または、レシーバを専用スレッドに移す)。 |
onReceive() の遅延 | すべてのレシーバ(ただし、主に同期レシーバ) | onReceive() メソッドは開始されたものの、ブロックされたか低速であるため、時間内に完了しなかった。 | レシーバコードの遅延を最適化する。 |
| 非同期レシーバタスクが未設定 | goAsync() レシーバ | onReceive() メソッドがブロックされたワーカー スレッド プールで処理を実行しようとしたため、処理が開始されなかった。 |
時間のかかる呼び出しやブロッキング呼び出しを最適化するか、ブロードキャスト ワーカーとその他の長時間実行タスクでスレッドを分ける。 |
| ワーカーが遅い、またはブロックされている | goAsync() レシーバ |
ブロードキャストの処理中に、ワーカー スレッド プールのどこかでブロッキング オペレーションまたは低速なオペレーションが発生した。そのため、PendingResult.finish が時間内に呼び出されなかった。 | async レシーバコードの遅延を最適化する。 |
PendingResult.finish の呼び出し漏れ |
goAsync() レシーバ |
finish() の呼び出しがコードパスから漏れている。 |
finish() が必ず呼び出されるようにする。 |
デバッグ方法
クラスタ シグネチャと ANR レポートに基づいて、レシーバが実行されているスレッドと、漏れているコードや時間がかかっているコードを特定します。
次のフローチャートは、ブロードキャスト レシーバ 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
getDataSyncis slow and optimize. - Don't run
getDataSyncon 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
goAsyncworker 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(), andonBind()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.
If you've determined that the execute service ANR is actionable, follow these steps to help resolve the issue:
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.MyServiceDetermine 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.handleBindApplicationApp 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 theMyServiceclass 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 を無視できます。
- ブロードキャスト レシーバなど、別のアプリ コンポーネントが実行されている。この場合、メインスレッドがこのコンポーネントでブロックされ、サービスを開始できない可能性があります。
重要な関数呼び出しを確認し、ANR がどこで発生しているかを特定できる場合は、残りのメインスレッド スタックを調べ、時間のかかるオペレーションを見つけて最適化するか、クリティカル パスから外します。
- 速やかにアプリが起動するようにします。アプリを起動してコンテンツ プロバイダを実行する場合、起動時間は ANR タイムアウトにカウントされます。
- コンテンツ プロバイダのクエリに時間がかからないようにします。
- アプリのすべてのバインダー スレッドをブロックする可能性があるため、同時にバインダー ブロッキング呼び出しを大量に実行しないでください。
- サイレント UI フリーズ: アプリの UI がユーザー入力に応答しなくなります。
- クラッシュ: アプリが
illegalStateExceptionでクラッシュする可能性があります。 - ANR: システムが ANR タイムアウトをトリガーする可能性があります。
- 走査のスケジュール設定:
ViewRootImplは、同期バリアを UI スレッドのMessageQueueに投稿することで、走査(レイアウトと描画パス)をスケジュールします。このバリアは、UI レイアウトが優先されるように、通常のメッセージ処理を一時停止します。 - 競合状態: 複数のスレッドが同時にビューを無効にしようとすると、このトラバーサルのスケジュール設定を競合します。両方のスレッドが 同期バリアを挿入できる可能性がありますが、フレームワークは一方のトークンのみを 保存します。
- UI のフリーズ: トラバーサルが実行されると、保存された単一のバリアのみが削除されます。「リーク」したセカンダリ バリアはキューに無期限に残るため、UI スレッドが同期メッセージを処理できなくなります。
Viewオブジェクトとのやり取り(widthやheightなどのプロパティの読み取り、TextView's textなどのプロパティの設定を含む)は、ビュー階層を作成したスレッドでのみ行ってください。これはほぼ常にメインスレッドまたは UI スレッドです。- ビューにアクセスするときに UI スレッドで実行されていることを保証できない場合は、安全でないと想定してください。たとえば、バックグラウンド処理に
Coroutinesを使用している場合は、Viewオブジェクトを操作する前にメイン ディスパッチャに切り替える必要があります。 ContextCompat#getMainExecutor(android.content.Context)View.post(Runnable)Activity.runOnUiThread(Runnable)- Android 17 以降を搭載したデバイスに、デバッグ可能なバージョンのアプリをインストールします。
- デバイスの設定アプリを開き、[システム] > [詳細設定] > [開発者向けオプション] > [アプリの互換性の変更] に移動します。
- リストからアプリを選択します。
- 変更点のリストから、
ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APISスイッチを探してオンにします。 - テレメトリーとトラッキング: このリスナーを使用すると、View API が誤ったスレッドから呼び出されたときにアプリがコールバックを受け取ることができるため、テレメトリーのログ記録とバグの追跡が容易になります。
- インライン実行: リスナーがインラインで呼び出されるため、違反が発生した瞬間の正確なスタック トレースをキャプチャして検査できます。
- システム全体の問題。システムの負荷が高いか、システム サーバーに問題が発生したため、プロセスがスケジュールされなかった場合です。
- スタックダンプの遅延。ANR のトリガーからスタックのダンプまでの短い時間に、スレッドが回復した場合です。Android 13 の Google Pixel のレイテンシは約 100 ミリ秒ですが、1 秒を超えることもあります。Android 14 の Google Pixel のレイテンシは通常 10 ミリ秒未満です。
- スレッドの誤り。ANR シグネチャの作成に使用されたスレッドが、ANR の原因である、実際に応答していないスレッドではなかった場合です。
- スレッド違反を表示。アプリがバックグラウンド スレッドでビューを変更すると、ビューの内部で競合状態が発生し、UI スレッドのタスクの実行がブロックされる可能性があります。
- システム負荷が高い: システム全体の CPU、メモリ、I/O の不足など、デバイス リソースの全体的な負荷を、応答なしの主な原因として評価します。
- スレッドの誤った割り当て: デッドロック、メインスレッドに影響するロック競合、非同期コンポーネント(
goAsync()など)を処理するバックグラウンド スレッドのハングアップについて、ワーカー スレッドとバインダ スレッドを監査します。 - スレッド違反を表示: バックグラウンド スレッドをスキャンして、違法な UI ビュー階層の変更を探します。このような変更は、MessageQueue の同期バリアを孤立させ、すべての同期メッセージを永久にブロックする可能性があります。
- スタックの取得に時間がかかりすぎてタイムアウトした場合。
- スタックの取得前に、プロセスが停止したか、強制終了された場合。
サービスの詳細については、次のページをご覧ください。
コンテンツ プロバイダの応答なし
コンテンツ プロバイダ ANR は、リモート コンテンツ プロバイダがタイムアウト時間内にクエリに応答できず、強制終了されると発生します。
デフォルトのタイムアウト時間: ContentProviderClient.setDetectNotResponding を使用してコンテンツ プロバイダが指定します。ANR タイムアウト時間には、リモート コンテンツ プロバイダ クエリの総実行時間が含まれます。その中には、リモートアプリがまだ実行されていない場合のコールド スタートも含まれます。
コンテンツ プロバイダ ANR が発生しないようにするには、次のおすすめの方法を参考にしてください。
一般的な原因
次の表は、コンテンツ プロバイダ ANR の一般的な原因と推奨される修正方法を示しています。
| 原因 | 現象 | シグナル | 推奨される修正方法 |
|---|---|---|---|
| コンテンツ プロバイダのクエリが遅い | コンテンツ プロバイダの実行に時間がかかりすぎているか、コンテンツ プロバイダがブロックされている。 | android.content.ContentProvider$Transport.query フレームがバインダー スレッド内にある。 |
コンテンツ プロバイダのクエリを最適化する。バインダー スレッドをブロックしているものを特定する。 |
| アプリの起動が遅い | コンテンツ プロバイダのアプリの起動に時間がかかりすぎている。 | ActivityThread.handleBindApplication フレームがメインスレッドにある。 |
アプリの起動を最適化する。 |
| バインダー スレッドの枯渇 - すべてのバインダー スレッドがビジー状態 | すべてのバインダー スレッドが他の同期リクエストを処理していてビジー状態のため、コンテンツ プロバイダのバインダー呼び出しを実行できない。 | アプリは起動しておらず、すべてのバインダー スレッドがビジー状態で、コンテンツ プロバイダが実行されていない。 | バインダー スレッドの負荷を軽減する。同期バインダー呼び出しを減らすか、呼び出しに対応する際の処理量を減らす。 |
デバッグ方法
Google Play Console または Firebase Crashlytics のクラスタ シグネチャと ANR レポートを使用してコンテンツ プロバイダ ANR をデバッグするには、メインスレッドとバインダー スレッドが実行している内容を確認します。
次のフローチャートは、コンテンツ プロバイダ 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() に問題がある場合は、できる限り速やかに呼び出すようにします。通知を送信する前に大量の処理を行わないでください。
スレッド違反を表示する
アプリがバックグラウンド スレッドでビューを変更すると、ビュー階層の内部状態に競合状態が発生する可能性があります。この違反により、メイン(UI)スレッドが同期メッセージを実行できなくなり、重大な安定性の問題が発生する可能性があります。
このスレッド処理違反は、メインスレッドがアイドル状態(「nativePollOnce」または「メインスレッドがアイドル状態」でキャプチャされたなど)に見える ANR スタック トレースの根本原因の 1 つです。
同期バリアのリーク
ビューは、状態をミューテーションすることによって間接的に(TextView.setText、View.setVisibility の呼び出しなど)、または View.invalidate または View.requestLayout を呼び出すことによって直接的に無効にできます。ビューが無効化されると、ViewRootImpl は、測定、レイアウト、描画を実行して UI の状態を更新するためのトラバーサルをスケジュール設定します。
その結果、UI がフリーズします。MessageQueue はリークされたバリアを越えて同期メッセージを処理できないため、メインスレッドはアイドル状態になります。通常、この問題はスタック トレースに nativePollOnce が含まれる ANR として表面化します。
ビュー スレッド違反を回避するには、次のおすすめの方法を参考にしてください。
lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { val data = myRepository.getData() withContext(Dispatchers.Main) { // Switch context to Main myTextView.text = data.title } }
ベスト プラクティスに沿って作業できるよう、Android では他のスレッドから UI スレッドにアクセスするための方法がいくつか用意されています。
一般的な画像ローダー、リアクティブ拡張機能フレームワーク、イベントバス ライブラリ、その他のスレッド処理ソリューションでは、通常、メインスレッドで特定のイベントを監視する方法が提供されています。これは、ビューの状態を取得して設定する必要があるオブザーバーやイベント リスナーに役立ちます。
デバッグ方法
Android 17 では、開発中にビュー スレッドの違反を特定、トレース、解決するのに役立つ新しいツールが導入されています。
1. 互換性フレームワーク ツール
互換性フレームワーク ツールを使用すると、アプリ デベロッパーは 開発者向けオプションまたは ADB を使用して、動作の変更を個別にオンまたはオフにできます。次の手順で、ビュー スレッド違反の動作変更を切り替えることができます。
ADB を使用してフラグをオンまたはオフにすることもできます。
$ adb shell am compat enable ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS <your.package.name>
$ adb shell am compat disable ENFORCE_THREAD_CHECKS_ON_VIEW_ROOT_IMPL_APIS <your.package.name>
このフラグを有効にすると、間違ったスレッドから View API が呼び出されるたびに、アプリが例外をスローしてクラッシュします。これにより、ビュー スレッド処理の違反問題を検出して修正できます。
2. CalledFromWrongThreadListener API
グローバル View#registerCalledFromWrongThreadListener API を実装して、不適切なスレッド アクセスをプログラムで検出することもできます。
android {
//...
compileSdk = 37
}
val listener = object : View.CalledFromWrongThreadListener { override fun onCalledFromWrongThread() { // Handle the issue, e.g. crash if this is a dev build, or log an event // e.g. Log.d(TAG, "CalledFromWrongThread: ${Exception().stackTraceToString()}") // Unregister the listener to avoid redundant notifications for the same issue View.unregisterCalledFromWrongThreadListener(this) } } View.registerCalledFromWrongThreadListener(listener)
nativePollOnce
ANR スタックにフレーム「nativePollOnce」または「message queue idle」が表示されている場合、多くのケースでは、応答していない疑いのあるスレッドがアイドル状態で、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)
応答していないと疑われるスレッドがアイドル状態である理由は次のとおりです。
「nativePollOnce」ANR の根本原因は ANR のタイプによって異なるため、特定の ANR カテゴリを診断することで、アプリ内の問題を解決するための実行可能な手順を特定できます。
| カテゴリ | 原因 | 修正案 |
|---|---|---|
| 入力ディスパッチのタイムアウト | システム全体の問題 スタック ダンプの遅延 |
対応は不要です。 |
| フォーカスされているウィンドウなし | システム全体の問題 スタック ダンプの遅延 スレッド違反の表示 |
ビュー スレッド処理の違反がないかコードベースを確認します。 |
| ブロードキャスト レシーバのタイムアウト | スタック ダンプの遅延 システム全体の問題 スレッドの誤った割り当て スレッド違反の表示 |
スタック ダンプ内の関連するスレッドを調べます。 ビュー スレッドの違反がないかコードベースを確認します。 |
| サービス実行のタイムアウト | スタック ダンプの遅延 システム全体の問題 スレッド違反の表示 |
ビュー スレッド処理の違反がないかコードベースを確認します。 |
| コンテンツ プロバイダの応答なし | 遅延スタック ダンプ システム全体の問題 スレッドの誤った割り当て |
スタック ダンプ内の関連するスレッドを調べます。 |
nativePollOnce 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 を非同期でモニタリングするため、正しく機能しない可能性があります。