Android開発におけるANRとは何か、原因、修正方法

著者: IT Sectr 公開日: 2026-03-28 読了時間: 9 分

ANR(Application Not Responding)は、アプリが5秒以上ユーザー入力に応答しなくなったときに表示されるAndroidシステム通知です。Android Developersによると、主な原因はメインスレッドでの長時間の操作により、タッチ処理とUIレンダリングがブロックされることです。応答性の高いアプリケーションを作成するには、すべてのAndroid開発者がANRのメカニズムを理解することが不可欠です。

重要なポイント

  • ANR — アプリが5秒以上フリーズしたときのAndroidシステム警告
  • メインスレッド(UIスレッド) — ブロッキングがANRにつながる唯一の場所
  • InputDispatcher — 入力遅延を検出してANRをトリガーするシステムコンポーネント
  • traces.txt — デバイス上のフリーズ原因を診断するためのキーファイル
  • StrictMode — 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解析なしで統計収集が簡素化されます。

ANRの主な原因

5つのカテゴリの操作がAndroidアプリで一貫してANRを引き起こします。それぞれがメインスレッドをブロックし、システムが入力イベントと画面の再描画を処理するのを防ぎます。

メインスレッドでのネットワークリクエスト

UIスレッドで実行される同期HTTPリクエストは、初心者開発者におけるANRの最も一般的な原因です。サーバーへの高速なリクエストでも1~3秒かかる可能性があり、接続が悪い場合は30秒以上かかることもあります。AndroidはAPI 11以降、メインスレッドでのネットワーク操作を明示的に禁止し、NetworkOnMainThreadExceptionをスローします。

非同期呼び出しにはCoroutinesまたはRxJavaを使用してください。Dispatchers.IOディスパッチャーを使用したコルーチンは、バックグラウンドスレッドでリクエストを実行し、Dispatchers.Mainを介して結果をメインスレッドに渡します。これにより、ネットワーク操作によるUIスレッドのブロッキングが完全に排除されます。

kotlin
fun fetchUserData() {
    CoroutineScope(Dispatchers.Main).launch {
        val result = withContext(Dispatchers.IO) {
            api.getUserData() // バックグラウンド操作
        }
        updateUI(result) // メインスレッドの結果
    }
}

UIスレッドでの集中計算

大きなデータ配列の処理、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

BroadcastReceiverはデフォルトでメインスレッドで実行されます。onReceive()が10秒以上ビジー状態の場合、AndroidはANRを表示します。onReceive内でデータベースやネットワークからデータをロードすることは、フリーズへの確実な道です。

バックグラウンドスレッドに切り替えるには、BroadcastReceiver内でgoAsync()を使用するか、getBackgroundBroadcastReceiver()を使用してregisterReceiverを呼び出してください。これにより、UIをブロックせずにイベントを処理できます。

メインスレッドでのContentProviderとSQLite

ContentProviderへの重いクエリやUIスレッドでのSQLiteの直接操作は、あまり明白ではないが一般的なANRの原因です。データベースの移行中や数千レコードの一括挿入中に、実行時間が5秒の制限を超える可能性があります。

すべてのデータベース操作は、suspend関数を使用してRoomを介してバックグラウンドスレッドに移動してください。Roomは自動的にクエリがメインスレッドで実行されていないかを確認し、違反した場合は例外をスローします。

ANRの診断方法

ANRの診断は通常の例外のデバッグとは異なります。try-catchブロックでANRをキャッチすることはできません。主な情報源は、Androidがフリーズ時に作成するtraces.txtファイルです。

traces.txtには、ANR時のすべてのアプリスレッドのコールスタックが含まれています。実際のデバイスからファイルを読み取るには、adb bugreportコマンドを実行します。これにより、最近のすべてのANRを含む完全なシステムレポートが収集されます。エミュレーターの場合、ファイルは/data/anr/traces.txtにあります。コールスタックは、ブロッキング時にメインスレッドで実行されていたメソッドを示します。

text
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の予防方法

ANRの予防は、1つの基本的なルールに基づいています。メインスレッドはUIイベントのみを処理する必要があります。16ミリ秒(1フレームの時間)を超える操作は、バックグラウンドスレッドで実行する必要があります。

StrictMode — 自動チェック

StrictModeは、開発中に潜在的なANRを検出するためのAndroid組み込みツールです。ディスクおよびネットワーク操作用のフラグを指定して、Application.onCreate()で有効にします。違反すると、StrictModeは例外をスローするか、logcatに書き込みます。

kotlin
if (BuildConfig.DEBUG) {
    StrictMode.ThreadPolicy.Builder()
        .detectDiskReads()
        .detectDiskWrites()
        .detectNetwork()
        .penaltyLog()
        .build()
        .let { StrictMode.setThreadPolicy(it) }
}

非同期パターン:CoroutinesとRxJava

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、システムによる強制終了)を提供します。コンテキスト分析のために、画面と状態のパラメーターを持つカスタムキーを有効にします。

ANR検出ツール

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 Monitoring

Firebase PerformanceはUIスレッドの応答時間を追跡し、不自然に長い操作に対して自動的にトレースを作成します。メインスレッドが500ミリ秒以上ブロックされると、Performanceは原因となったメソッドの名前でカスタムトレースを記録します。これにより、ユーザーが関与することなく、深刻化する前にANRシナリオを検出できます。

Firebase Crashlyticsとの統合により、完全な状況が得られます。PerformanceはANR前の遅延を表示し、Crashlyticsはフリーズ自体を表示します。Firebase ConsoleでANRレートが0.1%を超えた場合のアラートを設定すると、ユーザーからの大量の苦情が発生する前に新しい問題の通知を受け取れます。

よくある質問

ANRとCrashの違いは何ですか?

ANRはアプリが応答しないがメモリに残るフリーズです。Crashはプロセス終了を伴う完全な異常終了です。ANRはシステムまたはユーザーが応答を待てば「生き残る」可能性がありますが、Crashは常にアプリを終了させます。

try-catchでANRをキャッチできますか?

いいえ。ANRはJava/Kotlinの例外ではなく、プロセスレベルのシステムシグナル(SIGQUIT)です。開発者はアプリケーションコードでそれを処理できません。ANRに対応する唯一の方法は、再起動後にレポートを分析することです。

なぜANRは一部のデバイスで発生し、他のデバイスでは発生しないのですか?

デバイスのパフォーマンス、Androidバージョン、CPU負荷、バックグラウンドプロセスの数がANRの可能性に影響します。性能の低いデバイスでは、同じ操作が2~3倍長くかかり、5秒の制限を超える可能性があります。

ANR前のBroadcastReceiverの時間制限は?

通常のBroadcastReceiverのonReceive()では10秒です。フォアグラウンドサービスの制限は20秒で、ContentProviderには明示的な制限はありませんが、メインスレッドを5秒以上ブロックするとANRが発生します。

ANRがまれに発生し、再現できない場合はどうすればよいですか?

すべてのデバッグビルドでStrictModeを有効にし、Firebase Crashlyticsによる監視を追加し、ANRが発生したらadb bugreportを使用します。不定期のANRは、多くの場合、競合状態や特定のネットワーク状態に関連しています。

まとめ

  • ANR — メインスレッドが5秒以上ブロックされるとトリガーされるAndroidシステムメカニズム
  • メインスレッドはUIのみを処理する — 他のすべての操作はバックグラウンドスレッドに移される
  • ANRの診断はtraces.txt、Google Play Console、Firebase Crashlyticsを介して行われる
  • StrictModeは実際のデバイスで実行しなくても開発中に潜在的なANRを検出する
  • CoroutinesとDispatchers.IO — 最新のAndroidプロジェクトにおける非同期処理の標準的な方法
  • BroadcastReceiverが10秒以上動作するにはgoAsync()またはバックグラウンドレジストラが必要
  • 本番環境でのANRはCrashlyticsとAndroid 11以降の組み込みApplicationExitInfo APIを介して監視される

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください