このドキュメントでは、アプリが応答しているかどうかを Android システムがどのように判断しているかについて説明します。また、アプリの応答性を維持するための方法も紹介します。
適切に記述されたコードであっても、動作が遅い、ハングする、長期間フリーズする、入力処理に時間がかかりすぎるといった問題が生じる可能性があります。アプリがフォアグラウンドにあるのに応答しない場合、図 1 のようなアプリケーション応答なし(ANR)ダイアログがユーザーに表示されます。ユーザーは ANR ダイアログからアプリを強制終了できます。アプリがフォアグラウンドにない場合は、通知なく停止します。アプリの応答性を維持する設計をして、ANR ダイアログを最小限に抑えることが重要になります。
ANR トリガー
一般的に、アプリがメインスレッド(UI スレッドとも呼ばれます)でユーザー入力に応答せず、システムが受信したユーザー入力イベントを処理できない場合、ANR が表示されます。
たとえば、アプリが UI スレッドで I/O ブロッキング オペレーション(ネットワーク アクセスなど)を実行すると、ANR が発生することがあります。別の例としては、UI スレッドでアプリが複雑なメモリ内構造を構築したり、ゲームの次の動作を計算したりするのに膨大な時間がかかる場合が該当します。
Android では、アプリの応答性は、ActivityManager
および WindowManager
システム サービスでモニタリングされます。Android では、次のいずれかの状態が検出されると、アプリに ANR ダイアログを表示します。
- 入力イベント(キーの押下や画面タップなどのイベント)に対して、5 秒以内に応答がない。
- フォアグラウンド インテントの場合、
BroadcastReceiver
が 10~20 秒以内に実行を完了しない。詳細については、ブロードキャスト レシーバのタイムアウトをご覧ください。
ANR の回避
ANR を回避する一般的なおすすめの方法は次のとおりです。異なる種類の ANR の診断とデバッグの詳細については、このセクションの他のページをご覧ください。
メインスレッドは常にブロックされないようにして、スレッドを戦略的に使用します。
アプリのメインスレッドでブロッキング オペレーションや長時間実行オペレーションを実行しないようにします。代わりに、ワーカー スレッドを作成し、ほとんどの処理をワーカー スレッドで実行します。
メインスレッドと他のスレッド間のロックの競合を最小限に抑えるようにします。
ブロードキャストの処理やサービスの実行など、メインスレッドでの UI 以外の処理は最小限に抑えます。UI スレッドで実行するメソッドは、そのスレッドで実行する処理をできる限り少なくする必要があります。特にアクティビティでは、主なライフサイクル メソッド(
onCreate()
やonResume()
など)でのセットアップをできる限り最小限に抑えます。バックグラウンド スレッドでの処理のスケジュール設定と、UI との通信に使用できるソリューションについて詳しくは、バックグラウンド処理の概要をご覧ください。コンポーネント間でスレッドプールを共有する場合には注意します。長時間ブロッキングする可能性があるオペレーションと、ブロードキャストの受信などの時間的な制約があるタスクには、同じスレッドを使用しないでください。
アプリがすばやく起動するようにします。アプリの起動コードで時間のかかるオペレーションやブロッキング オペレーション(Dagger の初期化時に実行するメソッドなど)を最小限に抑えます。
BroadcastReceiver
を使用している場合は、Context.registerReceiver
を使用してメインスレッド以外でブロードキャスト レシーバを実行することを検討します。詳しくは、BroadcastReceiver の ANR をご覧ください。goAsync()
を使用する場合は、ANR タイムアウトの前にすばやくPendingResult.finish
が呼び出されるようにしてください。
BroadcastReceiver の ANR
ブロードキャスト レシーバは、設定の保存や Notification
の登録など、小規模な個別の処理をバックグラウンドで行うものであるため、BroadcastReceiver
の実行時間には制限があります。そのため、UI スレッドで呼び出される他のメソッドと同様に、長時間かかる可能性があるオペレーションや計算はブロードキャスト レシーバでは行わないようにする必要があります。長時間実行タスクは UI スレッドから実行するのではなく、後で実行されるようにバックグラウンドで実行します。有効なソリューションについて詳しくは、バックグラウンド処理の概要をご覧ください。
BroadcastReceiver
オブジェクトに関するもう一つの一般的な問題は、実行頻度が高すぎる場合に発生します。バックグラウンドで頻繁に実行すると、他のアプリが使用できるメモリの量が少なくなる可能性があります。BroadcastReceiver
オブジェクトを効率的に有効 / 無効にする方法について詳しくは、ブロードキャストの概要をご覧ください。
応答性の強化
一般的に、アプリの処理時間が 100~200 ミリ秒を超えると、ユーザーは遅いと感じるようになります。次の追加のヒントを確認し、ユーザーがアプリの応答性を高いと感じられるようにしてください。
アプリがユーザー入力に対する処理をバックグラウンドで行っている場合は、UI で
ProgressBar
などを使用して、処理が行われていることを示します。特にゲームの場合は、ワーカー スレッドで動作の計算を行います。
アプリの初期セットアップ フェーズに時間がかかる場合は、スプラッシュ画面を表示するか、メインビューをできる限り早くレンダリングすることを検討します。読み込みが進行中であることを示し、情報を非同期で表示するようにします。どちらの場合も、アプリがフリーズしているとユーザーが勘違いしないように、なんらかの方法で処理の進捗を示すことをおすすめします。
アプリの応答性におけるボトルネックを特定するには、Perfetto や CPU Profiler などのパフォーマンス ツールを使用します。