アプリの応答性を維持する

図 1.ユーザーに表示される ANR ダイアログ

このドキュメントでは、アプリが応答しているかどうかを Android システムがどのように判断しているかについて説明します。また、アプリの応答性を維持するための方法も紹介します。

適切に記述されたコードであっても、動作が遅い、ハングする、長期間フリーズする、入力処理に時間がかかりすぎるといった問題が生じる可能性があります。アプリがフォアグラウンドにあるのに応答しない場合、図 1 のようなアプリケーション応答なし(ANR)ダイアログがユーザーに表示されます。ユーザーは ANR ダイアログからアプリを強制終了できます。アプリがフォアグラウンドにない場合は、通知なく停止します。アプリの応答性を維持する設計をして、ANR ダイアログを最小限に抑えることが重要になります。

ANR トリガー

一般的に、アプリがメインスレッド(UI スレッドとも呼ばれます)でユーザー入力に応答せず、システムが受信したユーザー入力イベントを処理できない場合、ANR が表示されます。

たとえば、アプリが UI スレッドで I/O ブロッキング オペレーション(ネットワーク アクセスなど)を実行すると、ANR が発生することがあります。別の例としては、UI スレッドでアプリが複雑なメモリ内構造を構築したり、ゲームの次の動作を計算したりするのに膨大な時間がかかる場合が該当します。

Android では、アプリの応答性は、ActivityManager および WindowManager システム サービスでモニタリングされます。Android では、次のいずれかの状態が検出されると、アプリに ANR ダイアログを表示します。

ANR の回避

ANR を回避する一般的なおすすめの方法は次のとおりです。異なる種類の ANR の診断とデバッグの詳細については、このセクションの他のページをご覧ください。

  • メインスレッドは常にブロックされないようにして、スレッドを戦略的に使用します。

    • アプリのメインスレッドでブロッキング オペレーションや長時間実行オペレーションを実行しないようにします。 代わりに、Kotlin コルーチンを使用して、処理をバックグラウンド ディスパッチャ(Dispatchers.IODispatchers.Default など)にオフロードします。viewModelScope などのメカニズムを使用して、これらのバックグラウンド タスクを安全に起動します。また、LaunchedEffect を使用して、Compose の状態の変化に応じてトリガーします。

    • メインスレッドと他のスレッド間のロックの競合を最小限に抑えるようにします。

    • ブロードキャストの処理やサービスの実行など、メインスレッドでの UI 以外の処理は最小限に抑えます。UI スレッドで実行されるメソッドや関数は、できる限り少ない処理を行う必要があります。特にアクティビティでは、主なライフサイクル メソッド(onCreateonResume など)でのセットアップをできる限り最小限に抑えます。コンポーズ可能な関数内で I/O や負荷の高いブロッキング計算を直接実行しないでください。これにより、コンポジションと再コンポジション中に UI スレッドがブロックされます。バックグラウンド スレッドでの処理のスケジュール設定と、UI との通信に使用できるソリューションについて詳しくは、 バックグラウンド タスクの概要をご覧ください。

    • コンポーネント間でスレッドプールを共有する場合には注意します。長時間ブロッキングする可能性があるオペレーションと、ブロードキャストの受信などの時間的な制約があるタスクには、同じスレッドを使用しないでください。

  • アプリがすばやく起動するようにします。アプリの 起動コードで時間のかかるオペレーションやブロッキング オペレーション(依存関係挿入の設定( Hiltなど)中に実行されるメソッドや、 Jetpack の App Startup ライブラリを使用して初期化されるコンポーネントなど)を最小限に抑えます。 ベースライン プロファイル起動プロファイルR8 を使用して、アプリの起動をさらに最適化できます。

  • BroadcastReceiver を使用している場合は、 メインスレッド以外でブロードキャスト レシーバを実行することを検討します。Context.registerReceiver詳しくは、BroadcastReceiver の ANR をご覧ください。

    • goAsync を使用する場合は、ANR タイムアウトの前にすばやく PendingResult.finish が呼び出されるようにしてください。

BroadcastReceiver の ANR

ブロードキャスト レシーバは、設定の保存や Notification の登録など、小規模な個別の処理をバックグラウンドで行うものであるため、BroadcastReceiver の実行時間には制限があります。そのため、UI スレッドで呼び出される他のメソッドと同様に、長時間かかる可能性があるオペレーションや計算はブロードキャスト レシーバでは行わないようにする必要があります。長時間実行タスクは UI スレッドから実行するのではなく、後で実行されるようにバックグラウンドで実行します。有効なソリューションについて詳しくは、バックグラウンド タスクの概要をご覧ください。

BroadcastReceiver オブジェクトに関するもう一つの一般的な問題は、実行頻度が高すぎる場合に発生します。バックグラウンドで頻繁に実行すると、他のアプリが使用できるメモリの量が少なくなる可能性があります。BroadcastReceiver オブジェクトを効率的に有効 / 無効にする方法について詳しくは、ブロードキャストの概要をご覧ください。

応答性の強化

一般的に、アプリの処理時間が 100 ~ 200 ミリ秒を超えると、ユーザーは遅いと感じるようになります。次の追加のヒントを確認し、ユーザーがアプリの応答性を高いと感じられるようにしてください。

  • アプリがユーザー入力に対する処理をバックグラウンドで行っている場合は、 UI で CircularProgressIndicatorLinearProgressIndicator などを使用して、処理が行われていることを示します。

  • 特にゲームの場合は、バックグラウンド コルーチンまたはワーカー スレッドで動作の計算を行います。

  • アプリの初期セットアップ フェーズに時間がかかる場合は、 スプラッシュ画面を表示するか、最初のコンポジションをできる限り早く レンダリングすることを検討します。読み込みが進行中であることを示し、UI の状態を非同期で設定します。どちらの場合も、アプリがフリーズしているとユーザーが勘違いしないように、なんらかの方法で処理の進捗を示すことをおすすめします。

  • アプリの応答性におけるボトルネックを特定するには、PerfettoCPU Profiler などのパフォーマンス ツールを 使用します。