Android アプリの UI スレッドが長時間ブロックされると、アプリケーション応答なし(ANR)エラーが発生します。Android Vitals は、Google Play Console で ANR 指標を追跡してレポートします。これにより、さまざまなデバイスでアプリの安定性と応答性をモニタリングできます。
Android Vitals の指標としきい値
Android Vitals は、アプリの ANR 発生率をモニターして改善するために役立ちます。Android Vitals は、以下の ANR 発生率を測定します。
- ANR 発生率: 1 日のアクティブ ユーザーにつき、いずれかのタイプの ANR が発生した割合。
- ユーザーが認識した ANR 発生率: 1 日のアクティブ ユーザーにつき、ユーザーが認識した ANR が少なくとも 1 回発生した割合。現在、ユーザーが認識する ANR は
Input dispatching timed outタイプの ANR だけです。 - 複数回 ANR 発生率: 1 日のアクティブ ユーザーにつき、ANR が少なくとも 2 回発生した割合。
1 日のアクティブ ユーザーとは、1 日に 1 台のデバイスでアプリを使用するユニーク ユーザーを表し、複数のセッションにまたがる場合があります。1 人のユーザーが 1 日に複数のデバイスでアプリを使用する場合は、デバイスごとにその日のアクティブ ユーザーとしてカウントされます。
ユーザーが認識した ANR 発生率は「主な指標」の一つです。つまり、Google Play におけるアプリの見つけやすさに影響します。この指標は重要です。なぜなら、この指標でカウントされる ANR は、ユーザーがアプリを使用しているときに発生するものであり、重大な使用中断の原因となるからです。
Google Play では、この指標について 2 つの不正な動作のしきい値を定義しています。
- 全体的な不正な動作のしきい値: すべてのデバイスモデルで 1 日のアクティブ ユーザーが認識した ANR 発生率が 0.47% 以上。
- デバイスごとの不正な動作のしきい値: 1 つのデバイスモデルで 1 日のアクティブ ユーザーが認識した ANR 発生率が 8% 以上。
アプリが全体的な不正な動作のしきい値を超えると、すべてのデバイスでアプリの見つけやすさが低下する可能性があります。一部のデバイスでアプリがデバイスごとの不正な動作のしきい値を超えると、それらのデバイスでアプリの見つけやすさが低下し、ストアの掲載情報に警告が表示される可能性があります。
Android Vitals を使用すると、アプリで ANR が過度に発生するときに、Google Play Console を介してアラートを受け取ることができます。
Google Play が Android Vitals のデータを収集する方法については、Google Play Console のドキュメントをご覧ください。
問題を検出する
ANR が発生したときにアプリがフォアグラウンドにある場合、図 1 に示すように、ユーザーにダイアログが表示されます。ANR ダイアログにより、ユーザーはアプリを強制終了できます。
ANR が問題なのは、UI を更新する役割を担うアプリのメインスレッドがユーザー入力イベントの処理や描画を行うことができず、ユーザーに不満を感じさせるからです。アプリのメインスレッドの詳細については、プロセスとスレッドの概要をご覧ください。
次のいずれかの状況が発生すると、アプリで ANR がトリガーされます。
- 入力ディスパッチのタイムアウト: アプリが 5 秒以内に入力イベント(キーの押下や画面のタップなど)に応答しなかった場合。
- サービスの実行: アプリによって宣言されたサービスが数秒以内に
Service.onCreateとService.onStartCommandまたはService.onBindの実行を完了できなかった場合。 Service.startForegroundが呼び出されない: アプリがContext.startForegroundServiceを使用してフォアグラウンドで新しいサービスを開始したが、そのサービスが 5 秒以内にstartForegroundを呼び出さなかった場合。- インテントのブロードキャスト:
BroadcastReceiverが、設定された時間内に実行を完了しなかった場合。アプリにフォアグラウンドのアクティビティがある場合、タイムアウトは 10 秒です。 JobSchedulerの操作:JobServiceが数秒以内にJobService.onStartJobまたはJobService.onStopJobから戻らない場合、またはユーザーが開始するジョブが開始され、アプリがJobService.onStartJobの呼び出しから数秒以内にJobService.setNotificationを呼び出さなかった場合。Android 13 以前を対象とするアプリの場合、ANR は認識されず、アプリに報告されません。Android 14 以降を対象とするアプリでは、ANR は認識され、アプリに報告されます。
Android Vitals に加えて、他のツールを使用して本番環境で ANR を検出することもできますが、サードパーティ ツールは Android 10(API レベル 29)以前では ANR をレポートできません。
ANR を診断する
ANR を診断する際は、次のような一般的なパターンを探します。
- メインスレッドで I/O に関連するアプリの処理が遅くなっている。
- メインスレッドでアプリの計算に長時間かかっている。
- メインスレッドが別のプロセスへの同期バインダー呼び出しを行っており、その別のプロセスが復帰するまでに長時間かかっている。
- メインスレッドが、別のスレッドで発生している長時間実行オペレーションの同期ブロックを待機してブロックされている。
- メインスレッドが、プロセス内またはバインダー呼び出しを介して、別のスレッドとデッドロック状態になっている。メインスレッドは、単に長時間実行オペレーションが終了するのを待機しているのでなく、デッドロック状態になっています。
ANR の原因を特定するには、次の手法が有用です。
HealthStats
HealthStats は、ユーザーおよびシステムの合計時間、CPU 時間、ネットワーク、無線データ、画面のオン / オフ時間、ウェイクアップ アラームをキャプチャして、アプリケーションの健全性に関する指標を提供します。これは、全体的な CPU 使用率とバッテリーの消耗の測定で役立ちます。
デバッグ
Debug を使用すると、開発中の Android アプリを検査できます。これには、トレースや割り当てのカウントでアプリ内のジャンクや遅延を特定することなどが含まれます。Debug を使用して、ランタイム メモリとネイティブ メモリのカウンタ、および特定のプロセスのメモリ使用量を特定する際に役立つメモリ指標を取得することもできます。
ApplicationExitInfo
ApplicationExitInfo は Android 11(API レベル 30)以降で使用でき、アプリの終了の理由に関する情報を提供します。これには、ANR、メモリの不足、アプリのクラッシュ、CPU の過剰な使用、ユーザーの中断、システムの中断、実行時の権限の変更などが含まれます。
厳格モード
StrictMode を使用すると、アプリの開発中にメインスレッド上の誤った I/O オペレーションを見つけやすくなります。StrictMode は、アプリレベルまたはアクティビティ レベルで使用できます。
バックグラウンド ANR ダイアログを有効にする
Android では、デバイスの [開発者向けオプション] で [すべての ANR を表示] が有効になっている場合にのみ、ブロードキャスト メッセージの処理に時間がかかりすぎるアプリの ANR ダイアログが表示されます。そのため、アプリでパフォーマンスの問題が発生していても、必ずしもバックグラウンド ANR ダイアログがユーザーに表示されるとは限りません。
CPU とシステム トレースをプロファイリングする
Android Studio CPU Profiler を使用して、ゲームの実行中にシステム トレースと CPU アクティビティを記録します。ゲームエンジンのレンダリング スレッド、オーディオ スレッド、ワーカー スレッドとともにメインの Activity スレッドを検査すると、ANR が発生する前に、長いフレーム、同期アセットの読み込み、スレッドの競合を特定できます。
トレース ファイルを pull する
Android では、ANR が発生するとトレース情報が保存されます。以前の Android バージョンでは、デバイスに /data/anr/traces.txt ファイルが 1 つあります。Android の上位バージョンでは、複数の /data/anr/anr_* ファイルがあります。デバイスまたはエミュレータから ANR トレースにアクセスするには、ルートで Android Debug Bridge(adb)を使用します。
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
デバイスの [バグレポートを取得] 開発向けオプション、または開発マシンの adb bugreport コマンドを使用すると、実機からバグレポートを取得できます。詳細については、バグレポートのキャプチャと確認をご覧ください。
問題を解決する
問題を特定したら、このセクションのヒントを使用してよくある問題を解決できます。
メインスレッドのコードが遅い
アプリのメインスレッドが 5 秒を超えてビジー状態になっているコード内の場所を特定します。ゲーム内の疑わしいユースケースを探し、ANR を再現してみます。
ゲームでは、同期アセットの解凍、シェーダーのコンパイル、重いゲーム状態の初期化が Android のメイン Activity スレッド(onCreate、onResume、UI スレッドから呼び出される JNI コールバックなど)で実行されるときに、メインスレッドのストールがよく発生します。ゲームループの実行、アセット処理、シーンの初期化を、専用のエンジン、レンダリング、またはバックグラウンド ワーカー スレッドに移動して、メインスレッドが入力イベントとライフサイクル イベントを処理できるようにします。
メインスレッドの I/O
メインスレッドで I/O オペレーションを実行すると、メインスレッドでの処理が遅くなり、ANR が発生する可能性があります。
アセット バンドルの読み取り、ゲームの保存のシリアル化、SharedPreferences アクセス、ネットワーク リクエストをすべて、メインの Activity スレッドからバックグラウンド ワーカー スレッドに移動して、ストレージやネットワークのレイテンシによって入力ディスパッチがブロックされないようにします。
デッドロック
お互いに相手の保持するリソースを必要とする 2 つのスレッドがあり、スレッドが待機状態に入ると、デッドロックが発生します。アプリのメインスレッドがこの状況にある場合、ANR が発生する可能性があります。
ゲームでは、Android のメイン Activity スレッド(ライフサイクル、サーフェス、入力コールバックを処理)と、ネイティブ C / C++ のレンダリング、ロジック、オーディオ スレッドの間で、デッドロックやロック競合が頻繁に発生します。ネイティブ エンジンロックのメインスレッドでの同期待機を避け、JNI スレッドとエンジン スレッド間で通信する場合は、一貫したロック順序またはロックフリーのメッセージ キューを使用します。
デッドロックはコンピュータ サイエンスでよく研究されている現象で、デッドロックを回避するために使用できるデッドロック防止アルゴリズムがあります。
詳細については、Wikipedia のデッドロックとデッドロック防止アルゴリズムをご覧ください。
ブロードキャスト レシーバが遅い
アプリでブロードキャスト レシーバを使用すると、機内モードの有効化 / 無効化や接続状態の変更など、ブロードキャスト メッセージに応答できます。アプリでブロードキャスト メッセージの処理に時間がかかりすぎると、ANR が発生します。
ANR は次の場合に発生します。
- かなりの時間が経過してもブロードキャスト レシーバが
onReceiveメソッドの実行を完了しなかった。 - ブロードキャスト レシーバが
goAsyncを呼び出したが、PendingResultオブジェクトでfinishの呼び出しに失敗した。
アプリが BroadcastReceiver の onReceive メソッドで短いオペレーションのみを実行するようにする必要があります。ただし、ブロードキャスト メッセージの結果として、アプリがもっと複雑な処理を必要とする場合は、タスクに数秒しかかからないと予想される場合はバックグラウンド ワーカー スレッドにタスクを委ね、数秒以上かかると予想される場合は WorkManager にタスクを委ねる必要があります。
GameActivity
GameActivity ライブラリにより、C または C++ で記述されたゲームとアプリの事例研究で ANR が削減されました。既存のネイティブ アクティビティを GameActivity に置き換えると、UI スレッドのブロックを減らし、ANR の発生をある程度防ぐことができます。
Unity でゲームをビルドする場合は、Unity ゲームの ANR をデバッグする、Unity ゲームの一般的な ANR、Unity ゲームの Android のクラッシュと ANR をシンボリケートするをご覧ください。
ANR の詳細については、アプリの応答性を維持するをご覧ください。スレッドの詳細については、スレッド化によるパフォーマンスの向上をご覧ください。
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- 過度の wakeup