プロセスとアプリのライフサイクル

ほとんどの場合、どの Android アプリケーションも独自の Linux プロセスで動作します。このプロセスは、アプリケーションのコードの一部を実行する必要があるときに作成され、システムが他のアプリケーションで使用するためにメモリを再利用する必要があるときまで残ります。

Android の基本的な特徴のうち、一般的ではないものとして、アプリケーション プロセスの存続期間がアプリケーションによって直接制御されないということがあります。存続期間は、システムによって、実行中であることが認識されているアプリケーション、ユーザーにとっての重要度、システム全体での空きメモリ量の組み合わせから決定されます。

アプリケーション デベロッパーにとっては、さまざまなアプリケーション コンポーネント(特に、ActivityServiceBroadcastReceiver)がアプリケーションのプロセスの存続期間にどのように影響するかを理解することが重要です。これらのコンポーネントを正しく使用しないと、重要な作業を行っている間にシステムがアプリケーション プロセスを終了させる場合があります。

プロセスのライフサイクルに関連する不具合としてよくあるのは、BroadcastReceiverBroadcastReceiver.onReceive() メソッド内で Intent を受け取る際にスレッドを開始し、関数から戻ってしまう場合です。関数から戻ると、BroadcastReceiver が無効になったため、ホストプロセスが不要になった(プロセス中で他のアプリケーション コンポーネントが有効でない場合)とシステムにみなされます。

したがって、システムはメモリを再請求するためにいつでもプロセスを終了でき、プロセスで実行中の生成済みスレッドを終了させます。この問題への対応策は、BroadcastReceiver から JobService のスケジュールを設定するのが一般的です。そうすることで、プロセス内に有効な実行中処理が残っていることをシステムに認識させられます。

メモリが不足している場合に終了させるプロセスを決定するために、Android は実行中のコンポーネントとその状態に基づいて、各プロセスの重要度を階層中に位置付けます。プロセスには、次のタイプがあります(重要度の高い順)。

  1. フォアグラウンド プロセス: ユーザーが現在行っている作業に必要なプロセスです。さまざまなアプリケーション コンポーネントが、さまざまな方法で、そのプロセスをフォアグラウンドとみなされるようにします。次のいずれかの条件が満たされている場合、プロセスはフォアグラウンドであるとみなされます。
  2. このようなプロセスはシステム内にわずかしか存在せず、空きメモリが非常に少なくプロセスを実行し続けることができない場合にのみ、最後の手段として終了させられます。通常、この時点でデバイスはメモリ ページング状態になっているため、この動作はユーザー インターフェースの応答性を維持するために必要です。

  3. 表示プロセス: 処理がユーザーに認識されているプロセスです。そのため、終了させるとユーザー エクスペリエンスが大きく損なわれます。以下の条件に該当する場合、表示プロセスとみなされます。
    • フォアグラウンドではないが、画面上でユーザーに表示されている Activity を実行している(onPause() メソッドが呼び出されている)。たとえば、フォアグラウンドの Activity がダイアログとして表示されており、以前の Activity の背後に表示することが許可されている場合に起こります。
    • Service.startForeground() を使って、フォアグラウンド サービスとして実行している Service がある(サービスをユーザーが認識しているか、実質的に見えているものとして扱うように要求します)。
    • ライブ壁紙やインプット メソッド サービスなど、ユーザーに認識されている特定の機能のためにシステムが使用しているサービスをホストしている。

    実行中のプロセスの数は、フォアグラウンド プロセスほど制限されていませんが、ある程度管理されています。これらのプロセスは、非常に重要であるとみなされ、すべてのフォアグラウンド プロセスを実行し続けるために必要となる場合を除いて、終了されることはありません。

  4. サービス プロセス: startService() メソッドで開始された Service を保持しているプロセスです。これらのプロセスはユーザーに直接表示されませんが、通常はユーザーが関心を持つ処理(バックグラウンド ネットワーク データのアップロードやダウンロードなど)を行っています。そのため、フォアグラウンドと表示されているすべてのプロセスを保持するのに十分なメモリがない限り、システムは常にこのようなプロセスを実行し続けます。

    長時間実行されるサービス(30 分以上など)は重要度が下げられ、プロセスがキャッシュに保存されたリストに追加されるようになります。

    長時間実行する必要があるプロセスは、setForeground で作成できます。厳密な実行時間を必要とする定期的なプロセスの場合は、AlarmManager でスケジュールできます。詳細については、長時間実行ワーカーのサポートをご覧ください。これにより、メモリリークなどによりリソースを過剰に使用している長時間実行サービスが、システムで優れたユーザー エクスペリエンスを提供できない状況を回避できます。

  5. キャッシュ済みプロセス: その時点では必要ではなく、他の場所でメモリなどのリソースが必要な場合には自由に終了させることができるプロセスです。正常に動作するシステムでは、これらがリソース管理に関連する唯一のプロセスです。

    正常に実行されているシステムには、アプリケーション間の切り替えを効率的に行うため、常に利用可能なキャッシュ済みプロセスが複数あり、必要に応じてキャッシュに保存されたアプリが定期的に強制終了されます。すべてのキャッシュ済みプロセスを終了させ、さらにサービス プロセスの終了を始める必要があるのは、非常に重大な状況が生じた場合のみです。

    キャッシュに保存されたプロセスはシステムによっていつでも強制終了される可能性があるため、アプリはキャッシュに保存された状態の間はすべての処理を停止する必要があります。ユーザーにとって重要な処理をアプリで実行する必要がある場合は、上記のいずれかの API を使用して、アクティブなプロセス状態から処理を実行する必要があります。

    キャッシュに保存されたプロセスは、多くの場合、ユーザーに表示されていない(onStop() メソッドが呼び出されて戻った)Activity インスタンスを 1 つ以上保持します。システムがこのようなプロセスを強制終了するときに Activity ライフサイクルを正しく実装していれば、そのアプリに戻ったときのユーザー エクスペリエンスに影響はありません。関連するアクティビティが新しいプロセスで再作成されると、以前に保存された状態を復元できます。プロセスがシステムによって強制終了された場合、onDestroy() が呼び出されるとは限りません。詳しくは、Activity をご覧ください。

    Android 13 以降では、アプリプロセスが上記のアクティブなライフサイクル状態のいずれかに移行するまで、実行時間が制限されるか、実行時間がまったく割り当てられない場合があります。

    キャッシュに保存されたプロセスはリストに保持されます。このリストの正確な順序付けポリシーは、プラットフォームの実装の詳細です。通常、ユーザーのホーム アプリケーションをホストしているプロセスや最後に表示したアクティビティなど、より有用なプロセスを他の種類のプロセスより先に保持します。プロセスを終了させるポリシーとして、プロセス数のハードリミット、プロセスをキャッシュに保持できる時間の制限など、他のポリシーも適用できます。

プロセスの重要度付けの方法を決める際には、プロセスで現在有効なすべてのコンポーネントの中で最も重要なレベルに基づいて決定します。各コンポーネントがプロセスとアプリケーションのライフサイクル全体にどのように貢献しているかについて詳しくは、ActivityServiceBroadcastReceiver のドキュメントをご覧ください。

プロセスの他の依存関係に基づいて、プロセスの優先度を上げることもできます。たとえば、プロセス A が、プロセス B の Context.BIND_AUTO_CREATE フラグの付いた Service にバインドされているか、プロセス B の ContentProvider を使用している場合、プロセス B の重要度は最低でもプロセス A と同じになります。