ANR(Application Not Responding)は、アプリが5秒以上ユーザー入力に応答しなくなったときに表示されるAndroidシステム通知です。Android Developersによると、主な原因はメインスレッドでの長時間の操作により、タッチ処理とUIレンダリングがブロックされることです。応答性の高いアプリケーションを作成するには、すべてのAndroid開発者がANRのメカニズムを理解することが不可欠です。
重要なポイント
ANR(Application Not Responding)は、アプリがユーザー入力に応答しなくなったときに表示されるAndroidオペレーティングシステムのダイアログボックスです。システムはInputDispatcherを介してイベント処理時間を追跡します。タッチやボタン押下が5秒以内に処理されない場合、Androidはアプリを閉じるか待つかのダイアログを表示します。
ANRメカニズムは、フリーズしたアプリからユーザーエクスペリエンスを保護します。Androidは1つのアプリがシステム全体をブロックすることを許可しません。デスクトップOSとは異なり、モバイルプラットフォームは強制的にイベント処理時間を制限します。BroadcastReceiverには10秒の制限があり、フォアグラウンドサービスには20秒の制限があります。
ANRはコードの例外ではありません。Linuxプロセスレベルのシステムメカニズムです。AndroidはプロセスにSIGQUITシグナルを送信し、その後システムはすべてのスレッドのコールスタックをtraces.txtファイルに保存します。開発者はANRをcatch例外としてではなく、アプリ再起動後のレポートとして受け取ります。Android 11以降では、ApplicationExitInfo APIを使用して、ANRを含むプロセス終了の理由をプログラムで取得できます。これにより、手動でのtraces.txt解析なしで統計収集が簡素化されます。
5つのカテゴリの操作がAndroidアプリで一貫してANRを引き起こします。それぞれがメインスレッドをブロックし、システムが入力イベントと画面の再描画を処理するのを防ぎます。
UIスレッドで実行される同期HTTPリクエストは、初心者開発者におけるANRの最も一般的な原因です。サーバーへの高速なリクエストでも1~3秒かかる可能性があり、接続が悪い場合は30秒以上かかることもあります。AndroidはAPI 11以降、メインスレッドでのネットワーク操作を明示的に禁止し、NetworkOnMainThreadExceptionをスローします。
非同期呼び出しにはCoroutinesまたはRxJavaを使用してください。Dispatchers.IOディスパッチャーを使用したコルーチンは、バックグラウンドスレッドでリクエストを実行し、Dispatchers.Mainを介して結果をメインスレッドに渡します。これにより、ネットワーク操作によるUIスレッドのブロッキングが完全に排除されます。
fun fetchUserData() {
CoroutineScope(Dispatchers.Main).launch {
val result = withContext(Dispatchers.IO) {
api.getUserData() // バックグラウンド操作
}
updateUI(result) // メインスレッドの結果
}
}
大きなデータ配列の処理、JSONやXMLの解析、メインスレッドでのビットマップの直接操作は、ANRの2番目に頻繁な原因です。イベントループに戻らずにUIスレッドが300ミリ秒連続で動作するだけでも、レンダリングに顕著な遅延が発生し、5秒のしきい値に達するとANRとして記録されます。
WorkManagerとバックグラウンドサービスは、メインスレッドから重い計算を移すために設計されています。UIをブロックせずにデータをチャンクで渡すには、AsyncTask(非推奨)、ListenableFuture、またはKotlin Flowを使用してください。
デッドロックは、2つのスレッドがロックを保持し、互いに待機しているときに発生します。スレッドの1つがメインスレッドの場合、システムは正確に5秒後にANRを記録します。UIスレッドから呼び出されるThread.join()、CountDownLatch.await()、およびsynchronizedブロックはブロッキングのリスクをもたらします。
メインスレッドでのブロッキング操作は避けてください。synchronizedの代わりにConcurrentHashMapを、Thread.join()の代わりにasync/awaitを使用したコルーチンを使用してください。このルールはAndroidのすべての言語(Java、Kotlin、JNIを介したC++)に適用されます。
BroadcastReceiverはデフォルトでメインスレッドで実行されます。onReceive()が10秒以上ビジー状態の場合、AndroidはANRを表示します。onReceive内でデータベースやネットワークからデータをロードすることは、フリーズへの確実な道です。
バックグラウンドスレッドに切り替えるには、BroadcastReceiver内でgoAsync()を使用するか、getBackgroundBroadcastReceiver()を使用してregisterReceiverを呼び出してください。これにより、UIをブロックせずにイベントを処理できます。
ContentProviderへの重いクエリやUIスレッドでのSQLiteの直接操作は、あまり明白ではないが一般的なANRの原因です。データベースの移行中や数千レコードの一括挿入中に、実行時間が5秒の制限を超える可能性があります。
すべてのデータベース操作は、suspend関数を使用してRoomを介してバックグラウンドスレッドに移動してください。Roomは自動的にクエリがメインスレッドで実行されていないかを確認し、違反した場合は例外をスローします。
ANRの診断は通常の例外のデバッグとは異なります。try-catchブロックでANRをキャッチすることはできません。主な情報源は、Androidがフリーズ時に作成するtraces.txtファイルです。
traces.txtには、ANR時のすべてのアプリスレッドのコールスタックが含まれています。実際のデバイスからファイルを読み取るには、adb bugreportコマンドを実行します。これにより、最近のすべてのANRを含む完全なシステムレポートが収集されます。エミュレーターの場合、ファイルは/data/anr/traces.txtにあります。コールスタックは、ブロッキング時にメインスレッドで実行されていたメソッドを示します。
adb bugreport bugreport.zip
unzip -p bugreport.zip "*traces*" > traces.txt
Google Play Consoleには、集約されたレポートとエラー頻度を含むANR & Crashセクションがあります。各ANRについて、コールスタックとデバイス統計(モデル、Androidバージョン、地域)が表示されます。これにより、特定のデバイスやシステムバージョンに依存するANRを特定できます。
Android Studioは2021年以降、プロファイラーにANR Watchdogを搭載しています。メインスレッドがしきい値時間を超えて応答しない場合、自動的にスレッドダンプを記録します。このツールはイベントのタイムラインを表示します。どの操作が開始され、どのメソッドが実行され、どの段階でブロッキングが発生したかがわかります。
ANRの予防は、1つの基本的なルールに基づいています。メインスレッドはUIイベントのみを処理する必要があります。16ミリ秒(1フレームの時間)を超える操作は、バックグラウンドスレッドで実行する必要があります。
StrictModeは、開発中に潜在的なANRを検出するためのAndroid組み込みツールです。ディスクおよびネットワーク操作用のフラグを指定して、Application.onCreate()で有効にします。違反すると、StrictModeは例外をスローするか、logcatに書き込みます。
if (BuildConfig.DEBUG) {
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
.let { StrictMode.setThreadPolicy(it) }
}
Kotlin Coroutinesは、最新のAndroidアプリケーションにおける非同期処理の標準的な方法です。基本的なアプローチ:I/O操作はDispatchers.IOで実行され、結果はUI更新のためにDispatchers.Mainに渡されます。Flowのようなシナリオでは、CPU集中型タスクにDispatchers.Defaultを使用します。
RxJavaはレガシープロジェクトで引き続き人気があります。subscribeOn(Schedulers.io())とobserveOn(AndroidSchedulers.mainThread())は、ANRを防ぐための最小限のセットです。主要なルールは同じです。ObservableやFlowableはメインスレッドからデータを発行してはいけません。
Firebase CrashlyticsはSDKバージョン18.4.0以降、ANR監視を標準でサポートしています。Android 11以降では、CrashlyticsはシステムAPIのApplicationExitInfoを使用して、正確な終了理由(ANR、Crash、システムによる強制終了)を提供します。コンテキスト分析のために、画面と状態のパラメーターを持つカスタムキーを有効にします。
5つのツールがANR対応のすべての段階(ワークステーションでのデバッグから本番環境での監視まで)をカバーしています。各ツールは独自のタスクを解決し、さまざまなシナリオにデータを提供します。
| ツール | 目的 | データ形式 |
|---|---|---|
| StrictMode | 開発中の検出 | Logcat / Exception |
| ANR Watchdog(Android Studio) | リアルタイムトレース | Thread dump + timeline |
| Google Play Console | 集約統計 | ANR rate + stack traces |
| Firebase Crashlytics | 本番監視 | ApplicationExitInfo |
| adb bugreport | 完全なシステムレポート | traces.txt + logcat + dmesg |
各ツールには独自のニッチがあります。StrictModeは初期段階で明らかな違反を捕捉し、Crashlyticsはユーザー間の実際のANR頻度を示し、adb bugreportは複雑なケースに最も完全な状況を提供します。完全なカバレッジのためにこれらを組み合わせてください。
Firebase PerformanceはUIスレッドの応答時間を追跡し、不自然に長い操作に対して自動的にトレースを作成します。メインスレッドが500ミリ秒以上ブロックされると、Performanceは原因となったメソッドの名前でカスタムトレースを記録します。これにより、ユーザーが関与することなく、深刻化する前にANRシナリオを検出できます。
Firebase Crashlyticsとの統合により、完全な状況が得られます。PerformanceはANR前の遅延を表示し、Crashlyticsはフリーズ自体を表示します。Firebase ConsoleでANRレートが0.1%を超えた場合のアラートを設定すると、ユーザーからの大量の苦情が発生する前に新しい問題の通知を受け取れます。
よくある質問
ANRはアプリが応答しないがメモリに残るフリーズです。Crashはプロセス終了を伴う完全な異常終了です。ANRはシステムまたはユーザーが応答を待てば「生き残る」可能性がありますが、Crashは常にアプリを終了させます。
いいえ。ANRはJava/Kotlinの例外ではなく、プロセスレベルのシステムシグナル(SIGQUIT)です。開発者はアプリケーションコードでそれを処理できません。ANRに対応する唯一の方法は、再起動後にレポートを分析することです。
デバイスのパフォーマンス、Androidバージョン、CPU負荷、バックグラウンドプロセスの数がANRの可能性に影響します。性能の低いデバイスでは、同じ操作が2~3倍長くかかり、5秒の制限を超える可能性があります。
通常のBroadcastReceiverのonReceive()では10秒です。フォアグラウンドサービスの制限は20秒で、ContentProviderには明示的な制限はありませんが、メインスレッドを5秒以上ブロックするとANRが発生します。
すべてのデバッグビルドでStrictModeを有効にし、Firebase Crashlyticsによる監視を追加し、ANRが発生したらadb bugreportを使用します。不定期のANRは、多くの場合、競合状態や特定のネットワーク状態に関連しています。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。