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

ほとんどの場合、すべての Android アプリは独自の Linux プロセスで実行されます。このプロセスは、一部のコードの実行が必要になったときにアプリ用に作成され、他のアプリで使用するためにメモリの再利用が必要になり、そのメモリが不要になるまで実行され続けます。

Android の通常とは異なる基本的な特徴は、アプリプロセスの存続期間はアプリ自体によって直接制御されないことです。代わりに、実行中であることをシステムが認識しているアプリの部分、ユーザーにとってそれらの重要性、システムで使用できる全体的なメモリ量を組み合わせて、システムが決定します。

アプリ デベロッパーは、さまざまなアプリ コンポーネント(特に ActivityServiceBroadcastReceiver)がアプリのプロセスの存続期間にどのように影響するかを理解することが重要です。これらのコンポーネントを正しく使用していないと、重要な処理の実行中にアプリのプロセスが強制終了される可能性があります。

プロセスのライフサイクル バグの一般的な例としては、BroadcastReceiver.onReceive() メソッドで Intent を受け取ったときにスレッドを開始し、その後関数から戻る BroadcastReceiver があります。結果が返されると、他のアプリ コンポーネントがアクティブでない限り、BroadcastReceiver はもうアクティブでなく、そのホスティング プロセスが不要であると判断されます。

そのため、システムはいつでもプロセスを強制終了してメモリを再利用できます。その際に、プロセスで実行されている生成されたスレッドを終了します。通常、この問題の解決策は、BroadcastReceiver から JobService をスケジュールして、処理中にアクティブな処理が行われていることをシステムに知らせることです。

メモリが少ない場合に強制終了するプロセスを決定するため、Android では、実行中のコンポーネントとそれらのコンポーネントの状態に基づいて、各プロセスを重要度階層に振り分けます。これらのプロセスの種類は、重要なものから順に次のようになります。

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

  3. 可視プロセスは、ユーザーが現在認識している処理を実行するため、強制終了すると、ユーザー エクスペリエンスに著しい悪影響が及びます。プロセスは次の場合に可視とみなされます。
    • 画面上ではユーザーに表示され、フォアグラウンドでは表示されない Activity が実行されている(onPause() メソッドが呼び出された)。これはたとえば、フォアグラウンドの Activity が、前の Activity を背後に表示できるダイアログとして表示されている場合に発生することがあります。
    • Service.startForeground() を介してフォアグラウンド サービスとして実行されている Service がある(サービスをユーザーが認識しているものとして、または基本的に公開されているものとして扱うようシステムに要求する)。
    • ライブ壁紙やインプット メソッド サービスなど、ユーザーが認識している特定の機能のためにシステムが使用しているサービスをホストしている。

    システムで実行されるこれらのプロセスの数は、フォアグラウンド プロセスよりも制限されていませんが、それでも比較的制御されています。これらのプロセスはきわめて重要とみなされ、すべてのフォアグラウンド プロセスを実行し続けるために必要である場合を除き、強制終了されることはありません。

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

    長時間(30 分以上など)実行されているサービスの重要度が下がり、プロセスがキャッシュ リストから削除される可能性があります。

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

  5. キャッシュされたプロセス は現在不要なプロセスであり、メモリなどのリソースが別の場所で必要になった場合は、必要に応じて自由に強制終了できます。正常に動作しているシステムでは、リソース管理に関連するプロセスはこれらのプロセスだけです。

    正常に稼働しているシステムでは、アプリ間の切り替えを効率的に行うために、複数のキャッシュ プロセスが常に使用可能であり、必要に応じてキャッシュされたアプリを定期的に強制終了します。システムは、非常に重要な状況でのみ、キャッシュされたプロセスをすべて強制終了し、サービス プロセスの強制終了を開始する必要があります。

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

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

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

    キャッシュに保存されたプロセスはリストに保持される。このリストの正確な順序ポリシーは、プラットフォームの実装の詳細です。一般的には、ユーザーのホームアプリをホストするプロセスや、ユーザーが最後に見たアクティビティなど、より有用なプロセスを他のタイプのプロセスより先に保持しようとします。プロセスを強制終了するための他のポリシーも適用できます。たとえば、許可されるプロセス数のハードリミットを設定したり、プロセスが継続的にキャッシュに保存されている時間を制限したりできます。

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

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