アクティビティのライフサイクルについて

ユーザーがアプリの内外を移動してからアプリに戻ると、アプリ内の Activity インスタンスはライフサイクルのさまざまな状態の間を遷移します。 Activity クラスには、システムがアクティビティを作成、停止、再開している、またはアクティビティが存在するプロセスを破棄しているなど、状態が変化したことをアクティビティが認識できるようにするコールバックが備えられています。

ライフサイクル コールバック メソッド内で、ユーザーがアクティビティを離れてから戻った場合のアクティビティの動作を宣言できます。たとえば、ストリーミング動画プレーヤーを作成している場合は、ユーザーが別のアプリに切り替えたときに動画を一時停止し、ネットワーク接続を終了するようにできます。ユーザーが戻ってきた際に、ネットワークに再接続して、ユーザーが同じ場所から動画の再生を再開できるようにします。つまり、各コールバックにより、特定の状態変化に対して適した作業を実行できます。適切な作業を適切なタイミングで行い、移行を正しく処理することで、アプリの堅牢性とパフォーマンスが向上します。 たとえば、ライフサイクル コールバックを適切に実装することで、アプリで次の状況を回避できます。

  • アプリの使用中にユーザーが電話を受信したり、別のアプリに切り替えたりするとクラッシュする。
  • ユーザーが実際に使用していない場合に、貴重なシステム リソースが消費される。
  • ユーザーがアプリを離れてから後で戻ると、ユーザーの進捗状況が失われる。
  • 画面が横向きと縦向きの表示を切り替えている間に、クラッシュする、またはユーザーの進捗状況が失われる。

このドキュメントでは、アクティビティのライフサイクルについて詳しく説明します。最初にライフサイクル パラダイムについて説明します。次に、各コールバックに関して、実行時に内部で行われる処理と、その間に実装する必要がある対象について説明します。続いて、アクティビティの状態とシステムの強制終了に対するプロセスの脆弱性との関係を簡単に紹介します。 最後に、アクティビティの状態間の遷移に関連するトピックについて説明します。

おすすめの方法についてのガイダンスを含め、ライフサイクルの処理方法については、ライフサイクル対応コンポーネントによるライフサイクルの処理UI の状態の保存をご覧ください。 アクティビティをアーキテクチャ コンポーネントと組み合わせて使用することにより、製品版と同等の品質を備えた堅牢なアプリを設計する方法については、アプリのアーキテクチャ ガイドをご覧ください。

アクティビティのライフサイクルに関するコンセプト

アクティビティのライフサイクルにおけるステージ間を移動するために、Activity クラスには onCreate()onStart()onResume()onPause()onStop()onDestroy() の 6 つのコールバックのコアセットがあります。アクティビティが新しい状態になると、これらの各コールバックが呼び出されます。

図 1 は、このパラダイムを視覚的に示しています。

図 1. アクティビティのライフサイクルに関する簡略な図

ユーザーがアクティビティから離れる処理を開始すると、システムはアクティビティを解体するメソッドを呼び出します。場合によっては、この解体はあくまで部分的なものです。アクティビティは引き続きメモリ内にあり(ユーザーが別のアプリに切り替えた場合など)、フォアグラウンドに戻ることができます。ユーザーがアクティビティに戻ると、アクティビティはユーザーが中断したところから再開します。いくつかの例外はありますが、アプリはバックグラウンドでの実行中にアクティビティを開始できません

システムがシステム内にあるアクティビティとともに特定のプロセスを強制終了する可能性は、その時点でのアクティビティの状態によって異なります。アクティビティの状態とメモリからの退避では、状態と退避に対する脆弱性の関係について詳しく説明しています。

アクティビティの複雑さにもよりますが、すべてのライフサイクル メソッドを実装する必要はないと考えられます。ただし、それぞれを理解して、ユーザーの期待に応える動作をするアプリを実装することが重要です。

次のセクションでは、状態間の遷移を処理するために使用するコールバックについて詳しく説明します。

ライフサイクル コールバック

このセクションでは、アクティビティのライフサイクルで使用されるコールバック メソッドの概念と実装について説明します。

一部のアクション(setContentView() の呼び出しなど)は、アクティビティのライフサイクル メソッド自体に属します。ただし、依存コンポーネントのアクションを実装するコードはコンポーネント自体に配置する必要があります。これを実現するには、依存コンポーネントをライフサイクル対応にする必要があります。依存コンポーネントをライフサイクル対応にする方法については、ライフサイクル対応コンポーネントによるライフサイクルの処理をご覧ください。

onCreate()

このコールバックを実装する必要があります。このコールバックは、システムが最初にアクティビティを作成したときに呼び出されます。アクティビティが作成されると、アクティビティは作成済みの状態になります。 onCreate() メソッドでは、アクティビティのライフサイクルの全期間で 1 回のみ実行する必要がある基本的なアプリの起動ロジックを実行します。たとえば、onCreate() の実装では、データをリストにバインドし、アクティビティを ViewModel に関連付け、一部のクラススコープ変数をインスタンス化します。このメソッドはパラメータ savedInstanceState を受け取ります。これは、アクティビティの以前に保存された状態を含む Bundle オブジェクトです。アクティビティがこれまで存在しなかった場合、Bundle オブジェクトの値は null になります。

ライフサイクル対応コンポーネントがアクティビティのライフサイクルに接続されている場合は、ON_CREATE イベントを受け取ります。@OnLifecycleEvent アノテーションが付けられたメソッドが呼び出されるため、ライフサイクル対応コンポーネントは、必要な設定コードを作成された状態に対して実行できます。

次の onCreate() メソッドの例は、ユーザー インターフェースの宣言(XML レイアウト ファイルで定義)、メンバー変数の定義、一部の UI の構成など、アクティビティの基本設定を示しています。この例では、ファイルのリソース ID R.layout.main_activitysetContentView() に渡すことで XML レイアウト ファイルを指定しています。

Kotlin

lateinit var textView: TextView

// some transient state for the activity instance
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // call the super class onCreate to complete the creation of activity like
    // the view hierarchy
    super.onCreate(savedInstanceState)

    // recovering the instance state
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // set the user interface layout for this activity
    // the layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity)

    // initialize member TextView so we can manipulate it later
    textView = findViewById(R.id.text_view)
}

// This callback is called only when there is a saved instance that is previously saved by using
// onSaveInstanceState(). We restore some state in onCreate(), while we can optionally restore
// other state here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// invoked when the activity may be temporarily destroyed, save the instance state here
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // call superclass to save any view hierarchy
    super.onSaveInstanceState(outState)
}

Java

TextView textView;

// some transient state for the activity instance
String gameState;

@Override
public void onCreate(Bundle savedInstanceState) {
    // call the super class onCreate to complete the creation of activity like
    // the view hierarchy
    super.onCreate(savedInstanceState);

    // recovering the instance state
    if (savedInstanceState != null) {
        gameState = savedInstanceState.getString(GAME_STATE_KEY);
    }

    // set the user interface layout for this activity
    // the layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity);

    // initialize member TextView so we can manipulate it later
    textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance that is previously saved by using
// onSaveInstanceState(). We restore some state in onCreate(), while we can optionally restore
// other state here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY));
}

// invoked when the activity may be temporarily destroyed, save the instance state here
@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString(GAME_STATE_KEY, gameState);
    outState.putString(TEXT_VIEW_KEY, textView.getText());

    // call superclass to save any view hierarchy
    super.onSaveInstanceState(outState);
}

XML ファイルを定義して setContentView() に渡す代わりに、アクティビティ コード内に新しい View オブジェクトを作成し、新しい ViewViewGroup に挿入してビュー階層を作成することもできます。ルートの ViewGroupsetContentView() に渡してそのレイアウトを使用します。 ユーザー インターフェースの作成の詳細については、ユーザー インターフェースのドキュメントをご覧ください。

アクティビティは作成済みの状態にとどまりません。onCreate() メソッドの実行が終了すると、アクティビティは開始状態になり、システムは直ちに onStart() メソッドと onResume() メソッドを呼び出します。次のセクションでは、onStart() コールバックについて説明します。

onStart()

アクティビティが開始状態になると、システムはこのコールバックを呼び出します。アプリがアクティビティをフォアグラウンドに移動して操作可能な状態にする準備を行うと、onStart() の呼び出しによってアクティビティがユーザーに表示されます。 たとえば、このメソッドでは、UI を保持するコードをアプリが初期化します。

アクティビティが開始状態に移行すると、アクティビティのライフサイクルに関連付けられているライフサイクル対応コンポーネントは、ON_START イベントを受け取ります。

onStart() メソッドは直ちに完了し、作成済みの状態の場合と同様に、アクティビティは開始状態にとどまりません。このコールバックが終了すると、アクティビティは再開状態になり、システムは onResume() メソッドを呼び出します。

onResume()

アクティビティは再開状態になるとフォアグラウンドに移動し、システムは onResume() コールバックを呼び出します。これは、アプリがユーザーと対話する状態です。フォーカスがアプリから他の対象に移動する状況が発生するまで、アプリはこの状態を保持します。このようなイベントには、電話の着信、別のアクティビティへのユーザーの移動、デバイス画面をオフにする操作などがあります。

アクティビティが再開状態に移行すると、アクティビティのライフサイクルに関連付けられているライフサイクル対応コンポーネントは、すべて ON_RESUME イベントを受け取ります。この時点で、ライフサイクル コンポーネントは、コンポーネントが表示されフォアグラウンドにある状態で実行する必要がある機能(カメラ プレビューの開始など)を有効にできます。

割り込みイベントが発生すると、アクティビティは一時停止状態になり、システムは onPause() コールバックを呼び出します。

アクティビティが一時停止状態から再開状態に戻ると、システムは再び onResume() メソッドを呼び出します。このため、onPause() の実行中に解放したコンポーネントの初期化、およびアクティビティが再開状態になるたびに必要となるその他の初期化を実行するように、onResume() を実装する必要があります。

次の例は、コンポーネントが ON_RESUME イベントを受け取ると、カメラにアクセスするライフサイクル対応コンポーネントを示しています。

Kotlin

class CameraComponent : LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }

    ...
}

Java

public class CameraComponent implements LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void initializeCamera() {
        if (camera == null) {
            getCamera();
        }
    }

    ...
}

上記のコードは、LifecycleObserver が ON_RESUME イベントを受け取るとカメラを初期化します。ただし、マルチウィンドウ モードでは、一時停止状態のアクティビティも完全に表示される場合があります。たとえば、マルチウィンドウ モードで操作しているユーザーが、アクティビティが表示されていないもう一方のウィンドウをタップすると、アクティビティは一時停止状態になります。アプリが再開状態(フォアグラウンドで表示されアクティブな状態)になった場合にのみカメラをアクティブにするには、上記の ON_RESUME イベントの後にカメラを初期化します。アクティビティが一時停止状態であるものの表示されている場合(マルチウィンドウ モードになっている場合など)にカメラをアクティブな状態に保つには、ON_START イベントの後にカメラを初期化する必要があります。ただし、アクティビティが一時停止状態であるときにカメラをアクティブにすると、マルチウィンドウ モードで使用している別の再開状態のアプリでカメラへのアクセスが拒否される場合があります。アクティビティが一時停止状態になっている間にカメラをアクティブな状態で維持することが必要な場合もありますが、実際にはこのようにすると全般的なユーザーの操作性が低下します。マルチウィンドウを使用する状況で、ライフサイクルのどの時点において共有システム リソースを制御するのがより適切であるかを慎重に検討してください。マルチウィンドウ モードのサポートの詳細については、マルチウィンドウのサポートをご覧ください。

初期化操作を実行するビルドアップ イベントの選択にかかわらず、対応するライフサイクル イベントを使用してリソースを解放してください。ON_START イベントの後になんらかのアイテムを初期化する場合は、ON_STOP イベントの後に解放または終了してください。ON_RESUME イベントの後に初期化する場合は、ON_PAUSE イベントの後に解放してください。

上記のコード スニペットは、ライフサイクル対応コンポーネントにカメラ初期化コードを配置します。代わりに、このコードを onStart()onStop() などのアクティビティ ライフサイクルのコールバックに直接挿入することもできますが、この方法はおすすめしません。このロジックを独立したライフサイクル対応コンポーネントに追加すると、コードを複製せずにコンポーネントを複数のアクティビティで再利用できます。ライフサイクル対応コンポーネントの作成方法については、ライフサイクル対応コンポーネントによるライフサイクルの処理をご覧ください。

onPause()

ユーザーがアクティビティを離れることを最初に示すために、システムはこのメソッドを呼び出します(アクティビティが破棄されることを意味するとは限りません)。これは、アクティビティがフォアグラウンドにないことを示します(ユーザーがマルチウィンドウ モードの場合は、アクティビティが引き続き表示されます)。 Activity が一時停止状態であり、間もなく再開する予定である場合は、onPause() メソッドを使用して、続行しない(または適度に続行する)必要がある操作を一時停止または調整します。アクティビティは、いくつかの理由でこの状態になります。次に例を示します。

  • onResume() セクションの説明にあるように、一部のイベントがアプリの実行に割り込んだ。これは最も一般的なケースです。
  • Android 7.0(API レベル 24)以降において、複数のアプリがマルチウィンドウ モードで実行されている。一度に 1 つのアプリ(ウィンドウ)のみがフォーカスされるため、システムはその他のアプリをすべて一時停止します。
  • 新しい半透明のアクティビティ(ダイアログなど)が開く。対象のアクティビティが引き続き部分的に表示されているものの、フォーカスされていない場合は、一時停止状態が保持されます。

アクティビティが一時停止状態に移行すると、アクティビティのライフサイクルに関連付けられているライフサイクル対応コンポーネントは、ON_PAUSE イベントを受け取ります。この時点でライフサイクル コンポーネントは、コンポーネントがフォアグラウンドにないときに実行する必要のない機能を停止できます(カメラ プレビューの停止など)。

また、onPause() メソッドを使用して、システム リソース、(GPS などの)センサーの操作、またはアクティビティが一時停止されており、ユーザーが必要としない状態である間に電池寿命に影響を与える可能性があるリソースを解放することもできます。ただし、onResume() のセクションで前述したように、マルチウィンドウ モードでは、一時停止状態のアクティビティが引き続き完全に表示される場合があります。そのため、マルチウィンドウ モードのサポートが強化されるように、onPause() ではなく onStop() を使用して、UI 関連のリソースと操作を完全に解放または調整することを検討する必要があります。

ON_PAUSE イベントに対する LifecycleObserver のリアクションの例を以下に示します。これは、上記の ON_RESUME イベントの例に対応するものであり、ON_RESUME イベントを受け取った後に初期化されたカメラを解放します。

Kotlin

class CameraComponent : LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }

    ...
}

Java

public class JavaCameraComponent implements LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void releaseCamera() {
        if (camera != null) {
            camera.release();
            camera = null;
        }
    }

    ...
}

上記のコード スニペットでは、LifecycleObserver が ON_PAUSE イベントを受け取った後にカメラのリリースコードが配置されている点に留意してください。前述のとおり、ライフサイクル対応コンポーネントの作成方法については、ライフサイクル対応コンポーネントによるライフサイクルの処理をご覧ください。

onPause() の実行は非常に短時間で終了し、保存操作を実行するのに十分な時間を確保できない場合があります。このため、アプリデータまたはユーザーデータの保存、ネットワーク呼び出し、データベース トランザクションの実行には onPause() を使用しないでください。このような処理は、メソッドが完了するまでに完了しない可能性があります。代わりに、高負荷のシャットダウン操作は onStop() の実行中に行ってください。onStop() の実行中に行うのに適した操作の詳細については、onStop() をご覧ください。データの保存の詳細については、アクティビティの状態の保存と復元をご覧ください。

onPause() メソッドが完了しても、アクティビティが一時停止状態でなくなるわけではありません。むしろ、アクティビティが再開されるか、ユーザーに対して完全に非表示になるまで、アクティビティはこの状態のままになります。アクティビティが再開すると、システムは再び onResume() コールバックを呼び出します。アクティビティが一時停止状態から再開状態に戻ると、システムは Activity インスタンスをメモリ内に常駐させ、onResume() を呼び出す際にこのインスタンスを再呼び出しします。 このシナリオでは、再開状態に至ったいずれかのコールバック メソッドの間に作成されたコンポーネントを再初期化する必要はありません。アクティビティが完全に非表示になると、システムは onStop() を呼び出します。次のセクションでは、onStop() コールバックについて説明します。

onStop()

ユーザーに表示されなくなったアクティビティは、停止状態になり、システムは onStop() コールバックを呼び出します。これは、新たに開始されたアクティビティが画面全体を占有する場合などに発生します。アクティビティの実行が終了し、間もなく消滅する場合に、システムによって onStop() も呼び出されることがあります。

アクティビティが停止状態に移行すると、アクティビティのライフサイクルに関連付けられているライフサイクル対応コンポーネントは、ON_STOP イベントを受け取ります。この時点で、ライフサイクル コンポーネントは、コンポーネントが画面に表示されていない間に実行する必要のない機能を停止できます。

onStop() メソッドでは、アプリがユーザーに表示されていない間は不要なリソースを、アプリが解放または調整する必要があります。たとえば、アプリがアニメーションを一時停止する場合や、高精度の位置情報アップデートから低精度の位置情報アップデートに切り替える場合です。onPause() の代わりに onStop() を使用すると、ユーザーがマルチウィンドウ モードでアクティビティを表示している場合も、UI 関連の処理が続行されます。

また、CPU に比較的高い負荷をかけるシャットダウン操作を実行するには、onStop() を使用する必要があります。たとえば、データベースに情報を保存する適切なタイミングが見つからない場合は、onStop() でこの保存を実行できます。下書きのメモの内容を永続ストレージに保存する onStop() の実装例を次に示します。

Kotlin

override fun onStop() {
    // call the superclass method first
    super.onStop()

    // save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    val values = ContentValues().apply {
        put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
        put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
    }

    // do this update in background on an AsyncQueryHandler or equivalent
    asyncQueryHandler.startUpdate(
            token,     // int token to correlate calls
            null,      // cookie, not used here
            uri,       // The URI for the note to update.
            values,    // The map of column names and new values to apply to them.
            null,      // No SELECT criteria are used.
            null       // No WHERE columns are used.
    )
}

Java

@Override
protected void onStop() {
    // call the superclass method first
    super.onStop();

    // save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    // do this update in background on an AsyncQueryHandler or equivalent
    asyncQueryHandler.startUpdate (
            mToken,  // int token to correlate calls
            null,    // cookie, not used here
            uri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
    );
}

上記のサンプルコードでは SQLite が直接使用されている点に留意してください。代わりに SQLite に抽象化レイヤを提供する永続ライブラリである Room を使用してください。Room を使用するメリットと、Room をアプリで実装する方法については、Room 永続ライブラリのガイドをご覧ください。

アクティビティが停止状態になると、Activity オブジェクトがメモリ内に常駐します。すべての状態とメンバー情報が保持されますが、ウィンドウ マネージャーには接続されません。アクティビティが再開すると、アクティビティはこの情報を再呼び出しします。再開状態に至ったいずれかのコールバック メソッドの間に作成されたコンポーネントを再初期化する必要はありません。また、システムはレイアウト内の各 View オブジェクトの現在の状態を追跡するため、ユーザーが EditText ウィジェットにテキストを入力すると、そのコンテンツが保持されることから、保存および復元する必要はありません。

注: アクティビティを停止すると、システムはメモリを回復する必要がある場合に、アクティビティを含むプロセスを破棄することがあります。 アクティビティの停止中にシステムがプロセスを破棄した場合でも、システムは View オブジェクトの状態(EditText ウィジェットのテキストなど)を引き続き Bundle(キーと値のペアの blob)内に保持し、ユーザーがアクティビティに戻った際に復元します。ユーザーが戻ったアクティビティの復元の詳細については、アクティビティの状態の保存と復元をご覧ください。

停止状態のアクティビティは、その後ユーザーとやり取りするために戻るか、実行が終了して消滅します。アクティビティが戻ると、システムは onRestart() を呼び出します。 Activity の実行が終了すると、システムは onDestroy() を呼び出します。次のセクションでは、onDestroy() コールバックについて説明します。

onDestroy()

onDestroy() はアクティビティが破棄される前に呼び出されます。このコールバックは、次のいずれかの理由でシステムによって呼び出されます。

  1. アクティビティが終了する(ユーザーがアクティビティを完全に閉じるか、アクティビティに対して finish() が呼び出さたことによる)。
  2. 構成の変更(デバイスの向きの変更やマルチウィンドウ モードなど)に伴いアクティビティが一時的に破棄される。

アクティビティが破棄状態に移行すると、アクティビティのライフサイクルに関連付けられているライフサイクル対応コンポーネントは ON_DESTROY イベントを受け取ります。この時点で、アクティビティが破棄される前にクリーンアップする必要があるデータをライフサイクル コンポーネントがクリーンアップできます。

破棄される理由を判別するロジックをアクティビティに挿入する代わりに、ViewModel オブジェクトを使用してアクティビティの関連ビューデータを格納する必要があります。構成の変更に伴いアクティビティが再作成される場合、アクティビティは保持され、次のアクティビティ インスタンスに渡されることから、ViewModel がなんらかの処理を行う必要はありません。アクティビティが再作成されない場合は、ViewModel で onCleared() メソッドが呼び出されます。ここで、破棄する前にクリーンアップする必要があるデータをクリーンアップできます。

これらの 2 つのシナリオは、isFinishing() メソッドで区別できます。

アクティビティが終了する場合、onDestroy() はアクティビティが受け取る最後のライフサイクル コールバックです。構成の変更の結果として onDestroy() が呼び出される場合、システムは新しいアクティビティ インスタンスを即時に作成し、新しい構成の新しいインスタンスに対して onCreate() を呼び出します。

以前のコールバック(onStop() など)によって解放されていないすべてのリソースは、onDestroy() コールバックによって解放されます。

アクティビティの状態とメモリからの退避

システムは RAM を解放する必要がある場合にプロセスを強制終了します。システムが特定のプロセスを強制終了する可能性は、その時点でのプロセスの状態に応じて異なります。同様に、プロセスの状態は、プロセスで実行されているアクティビティの状態によって異なります。 表 1 に、プロセスの状態、アクティビティの状態、およびシステムがプロセスを強制終了する可能性の相関関係を示します。

強制終了の可能性 プロセスの状態 アクティビティの状態
フォアグラウンド(フォーカスがあるか、間もなくフォーカスを取得する) 作成
開始
再開
バックグラウンド(フォーカスがない) 一時停止
バックグラウンド(非表示) 停止
破棄

表 1. プロセスのライフサイクルとアクティビティの状態の関係

システムがメモリを解放するためにアクティビティを直接強制終了することはありません。代わりに、アクティビティが実行されているプロセスを強制終了します。これにより、アクティビティのみでなくプロセスで実行されているすべての内容が破棄されます。システムによって開始されたプロセスが終了した場合にアクティビティの UI の状態を保存および復元する方法については、アクティビティの状態の保存と復元をご覧ください。

ユーザーは、アプリケーション マネージャーの [設定] でプロセスを強制終了し、対応するアプリを強制終了することもできます。

一般的なプロセスの詳細については、プロセスとスレッドをご覧ください。 プロセスのライフサイクルとプロセス内にあるアクティビティの状態との関連付けについて詳しくは、該当ページのプロセスのライフサイクルのセクションをご覧ください。

一時的な UI の状態の保存と復元

ユーザーは、構成変更(回転やマルチウィンドウ モードへの切り替えなど)全般にわたってアクティビティの UI の状態が変わらないことを想定します。ただし、このような構成変更が行われると、システムはデフォルトでアクティビティを破棄し、アクティビティ インスタンスに保存されている UI の状態を消去します。 同様に、ユーザーは一時的に別のアプリに切り替えてから元のアプリに戻った場合にも、UI の状態が同じままであることを想定します。ただし、システムはユーザーが離れてアクティビティが停止している間に、アプリのプロセスを破棄する場合があります。

システムの制約が原因でアクティビティが破棄される場合は、ViewModelonSaveInstanceState()、およびローカル ストレージを組み合わせて使用して、ユーザーの一時的な UI の状態を保持する必要があります。ユーザーが想定する内容とシステムの動作の比較、およびシステムが開始したアクティビティとプロセスの終了時に複雑な UI の状態のデータを保持する最適な方法については、UI の状態を保存するをご覧ください。

このセクションでは、インスタンスの状態と onSaveInstance() メソッド(アクティビティ自体に対するコールバック)の実装方法について概説します。UI データが単純で軽量な場合(例: プリミティブ データ型または String などの単純なオブジェクトの場合)は、onSaveInstanceState() を単独で使用して、構成の変更とシステムが開始したプロセスの終了の両方に対して UI の状態を保持できます。ただし、ほとんどの場合は、UI の状態を保存するで概説しているように、ViewModel と onSaveInstanceState() の両方を使用する必要があります。これは、onSaveInstanceState() によりシリアル化 / シリアル化解除のコストが発生するためです。

インスタンスの状態

通常のアプリの動作によって、アクティビティが破棄されるシナリオがいくつかあります。たとえば、ユーザーが [戻る] ボタンを押す、またはアクティビティが finish() メソッドを呼び出すことによって自身の破棄を知らせる場合が考えられます。ユーザーが [戻る] を押すか、アクティビティ自体が終了したためにアクティビティが破棄されると、システムとユーザー両方の Activity インスタンスのコンセプトは完全に失われます。これらのシナリオでは、ユーザーの想定とシステムの動作が一致しており、追加の作業は不要です。

ただし、システムの制約(構成の変更やメモリ負荷など)が原因でアクティビティが破棄された場合、実際の Activity インスタンスは失われますが、システムにはこのアクティビティが存在したことが記憶されます。ユーザーがアクティビティに戻ろうとすると、システムはアクティビティが破棄された時点でのアクティビティの状態を記述する一連の保存済みデータを使用して、アクティビティの新しいインスタンスを作成します。

システムが以前の状態を復元するために使用する保存済みデータは、インスタンス状態と呼ばれ、Bundle オブジェクトに格納されたキーと値のペアの集合です。デフォルトでは、システムは Bundle インスタンス状態を使用して、アクティビティのレイアウトの各 View オブジェクトに関する情報(EditText ウィジェットに入力されたテキスト値など)を保存します。そのため、アクティビティのインスタンスが破棄されてから再作成された場合、レイアウトの状態はコードを必要とすることなく以前の状態に復元されます。ただし、アクティビティには、アクティビティ内のユーザーの作業状況を追跡するメンバー変数など、復元する必要があると考えられる状態に関する情報が他にも含まれている場合があります。

注: Android システムがアクティビティのビューの状態を復元できるようにするには、各ビューに一意の ID が必要です。この ID は android:id 属性で指定します。

Bundle オブジェクトは、メインスレッドでのシリアル化を必要とし、システムプロセス メモリを消費するため、ごく少量のデータを保存する場合を除いて、データの保存には適していません。保持するデータ量が非常に少ない場合以外は、UI の状態の保存で概説したように、永続ローカル ストレージ、onSaveInstanceState() メソッド、ViewModel クラスを組み合わせた方法でデータを保持する必要があります。

onSaveInstanceState() を使用してシンプルで軽量な UI の状態を保存する

アクティビティの停止が開始されると、システムは onSaveInstanceState() メソッドを呼び出します。これにより、アクティビティは状態情報をインスタンス状態バンドルに保存できます。このメソッドのデフォルトの実装では、EditText ウィジェット内のテキストや ListView ウィジェットのスクロール位置など、アクティビティのビュー階層の状態に関する一時的な情報が保存されます。

アクティビティの追加のインスタンス状態情報を保存するには、onSaveInstanceState() をオーバーライドし、アクティビティが予期せず破棄された際に保存された Bundle オブジェクトにキーと値のペアを追加します。onSaveInstanceState() をオーバーライドする場合、デフォルトの実装でビュー階層の状態を保存するには、スーパークラス実装を呼び出す必要があります。 次に例を示します。

Kotlin

override fun onSaveInstanceState(outState: Bundle?) {
    // Save the user's current game state
    outState?.run {
        putInt(STATE_SCORE, currentScore)
        putInt(STATE_LEVEL, currentLevel)
    }

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(outState)
}

companion object {
    val STATE_SCORE = "playerScore"
    val STATE_LEVEL = "playerLevel"
}

Java

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
// ...

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, currentScore);
    savedInstanceState.putInt(STATE_LEVEL, currentLevel);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

注: ユーザーがアクティビティを明示的に閉じた場合、または finish() が呼び出された場合には、onSaveInstanceState() は呼び出されません。

ユーザー設定やデータベースのデータなどの永続データを保存するには、アクティビティがフォアグラウンドにある適切な時点で保存してください。 このような適切な時点がない場合は、onStop() メソッドの実行中にデータを保存してください。

保存済みのインスタンスの状態を使用してアクティビティの UI の状態を復元する

以前に破棄したアクティビティを再作成する場合は、システムがアクティビティに渡した Bundle から保存済みのインスタンスの状態を復元できます。コールバック メソッド onCreate()onRestoreInstanceState() は両方とも、同じ Bundle を受け取り、これにはインスタンスの状態情報が含まれています。

onCreate() メソッドは、システムがアクティビティの新しいインスタンスを作成している場合、または以前のインスタンスを再作成している場合のいずれであっても呼び出されるため、状態バンドルを読み取る前にこれが null かどうかを確認する必要があります。null の場合は、破棄された以前のインスタンスが復元されるのではなく、アクティビティの新しいインスタンスが作成されます。

たとえば、次のコード スニペットは、onCreate() で一部の状態データを復元する方法を示しています。

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState) // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
        with(savedInstanceState) {
            // Restore value of members from saved state
            currentScore = getInt(STATE_SCORE)
            currentLevel = getInt(STATE_LEVEL)
        }
    } else {
        // Probably initialize members with default values for a new instance
    }
    // ...
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
        // Restore value of members from saved state
        currentScore = savedInstanceState.getInt(STATE_SCORE);
        currentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance
    }
    // ...
}

onCreate() の実行中に状態を復元する代わりに、onStart() メソッドの後にシステムが呼び出す onRestoreInstanceState() を実装することもできます。システムは、復元対象の保存済みの状態がある場合にのみ onRestoreInstanceState() を呼び出すため、Bundle が null であるかどうかを確認する必要はありません。

Kotlin

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState)

    // Restore state members from saved instance
    savedInstanceState?.run {
        currentScore = getInt(STATE_SCORE)
        currentLevel = getInt(STATE_LEVEL)
    }
}

Java

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from saved instance
    currentScore = savedInstanceState.getInt(STATE_SCORE);
    currentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

注意: デフォルトの実装でビュー階層の状態を復元できるよう、onRestoreInstanceState() のスーパークラスの実装を常に呼び出す必要があります。

アクティビティ間の移動

アプリの存続期間中に、アプリはアクティビティに入り、アクティビティから出る操作を何度も行う可能性があります。たとえば、ユーザーがデバイスの [戻る] ボタンをタップする、またはアクティビティが別のアクティビティを開始する必要がある場合が考えられます。このセクションでは、アクティビティの適切な遷移を実装するうえで理解しておく必要があるトピックについて説明します。 これらのトピックには、別のアクティビティからのアクティビティの開始、アクティビティの状態の保存、アクティビティの状態の復元などがあります。

アクティビティを別のアクティビティから開始する

特定の時点で、アクティビティが別のアクティビティを開始する必要が生じることは頻繁にあります。たとえば、アプリが現在の画面から新しい画面に移動する必要がある場合に、この動作が必要です。

実行中のアクティビティで、新たに開始しようとしているアクティビティから結果を戻す必要があるかどうかに応じて、startActivity() メソッドまたは startActivityForResult() メソッドのいずれかを使用して新しいアクティビティを開始します。いずれの場合も、Intent オブジェクトを渡します。

Intent オブジェクトは、開始するアクティビティを正確に指定するか、実行する操作のタイプを記述します(システムが適切なアクティビティを選択しますが、そのアクティビティが他のアプリのアクティビティである場合も考えられます)。Intent オブジェクトには、開始したアクティビティで使用する少量のデータを含めることもできます。 Intent クラスの詳細については、インテントとインテント フィルタをご覧ください。

startActivity()

新規に開始されたアクティビティから結果を返す必要がない場合、現在のアクティビティは startActivity() メソッドを呼び出して新しいアクティビティを開始できます。

独自のアプリで作業する場合には、既知のアクティビティを起動するだけで済むことが頻繁にあります。 たとえば、次のコード スニペットは、SignInActivity という名前のアクティビティを起動する方法を示しています。

Kotlin

val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)

Java

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

アクティビティから取得したデータを使用して、アプリでメールやテキスト メッセージの送信、ステータスの更新などの操作を行うことが必要な場合もあります。 アプリにそのような操作を実行できる独自のアクティビティがない場合は、代わりにデバイス上の他のアプリから提供された、該当の操作を実行可能なアクティビティを活用できます。ここがインテントが真価を発揮する場面です。実行する操作を記述するインテントを作成し、システムが別のアプリから適切なアクティビティを起動します。インテントを処理できるアクティビティが複数ある場合は、ユーザーが使用するアクティビティを選択できます。たとえば、ユーザーがメール メッセージを送信できるようにするには、次のインテントを作成します。

Kotlin

val intent = Intent(Intent.ACTION_SEND).apply {
    putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)

Java

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

インテントに追加された EXTRA_EMAIL のエクストラは、メールの送信先となるメールアドレスの文字列配列です。メールアプリがこのインテントに応答すると、エクストラにある文字列配列を読み取り、メール作成フォームの「宛先」フィールドに配置します。この場合、メールアプリのアクティビティが開始され、ユーザーが操作を完了するとアクティビティが再開されます。

startActivityForResult()

アクティビティの終了時に、アクティビティから結果を戻すことが必要な場合があります。たとえば、ユーザーが連絡先リストから担当者を選択できるようにするアクティビティを開始した場合、このアクティビティは終了時に、選択された担当者を返します。これを行うには、startActivityForResult(Intent, int) メソッドを呼び出します。このメソッドでは、整数パラメータにより呼び出しが識別されます。この ID によって、同一のアクティビティからの複数の startActivityForResult(Intent, int) 呼び出しを明確に区別できます。この ID はグローバル ID ではないため、他のアプリやアクティビティと競合するリスクはありません。結果は onActivityResult(int, int, Intent) メソッドにより返されます。

子アクティビティが終了すると、setResult(int) を呼び出して親にデータを返すようにすることができます。 子アクティビティは常に結果コードを提供する必要があります。結果コードには標準の結果コード RESULT_CANCELEDRESULT_OK、または RESULT_FIRST_USER から始まるカスタム値を使用できます。また、子アクティビティはオプションとして、必要な追加データを含む Intent オブジェクトを返すこともできます。親アクティビティは、onActivityResult(int, int, Intent) メソッドとともに、親アクティビティが当初指定した整数の ID を使用して情報を受け取ります。

クラッシュなど、なんらかの原因で子アクティビティが失敗すると、親アクティビティはコード RESULT_CANCELED の結果を受け取ります。

Kotlin

class MyActivity : Activity() {
    // ...

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            // When the user center presses, let them pick a contact.
            startActivityForResult(
                    Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")),
                    PICK_CONTACT_REQUEST)
            return true
        }
        return false
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
        when (requestCode) {
            PICK_CONTACT_REQUEST ->
                if (resultCode == RESULT_OK) {
                    startActivity(Intent(Intent.ACTION_VIEW, intent?.data))
                }
        }
    }

    companion object {
        internal val PICK_CONTACT_REQUEST = 0
    }
}

Java

public class MyActivity extends Activity {
     // ...

     static final int PICK_CONTACT_REQUEST = 0;

     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             // When the user center presses, let them pick a contact.
             startActivityForResult(
                 new Intent(Intent.ACTION_PICK,
                 new Uri("content://contacts")),
                 PICK_CONTACT_REQUEST);
            return true;
         }
         return false;
     }

     protected void onActivityResult(int requestCode, int resultCode,
             Intent data) {
         if (requestCode == PICK_CONTACT_REQUEST) {
             if (resultCode == RESULT_OK) {
                 // A contact was picked.  Here we will just display it
                 // to the user.
                 startActivity(new Intent(Intent.ACTION_VIEW, data));
             }
         }
     }
 }

アクティビティを連携させる

あるアクティビティが別のアクティビティを開始すると、双方でライフサイクルの遷移が発生します。 最初のアクティビティが動作を停止して、一時停止状態または停止状態となり、もう 1 つのアクティビティが作成されます。これらのアクティビティでディスクなどに保存されているデータを共有している場合は、2 つ目のアクティビティが作成される前に最初のアクティビティが完全に停止することはない点を理解しておくことが重要です。むしろ、2 つ目のアクティビティを開始するプロセスは、最初のアクティビティを停止するプロセスと重複します。

ライフサイクル コールバックの順序は、特に 2 つのアクティビティが同じプロセス(アプリ)にあり、一方がもう一方のアクティビティを開始する場合に厳密に定義されます。アクティビティ A がアクティビティ B を開始する場合の動作の順序は次のとおりです。

  1. アクティビティ A の onPause() メソッドが実行されます。
  2. アクティビティ B の onCreate()onStart()onResume() の各メソッドが順次実行されます (このとき、アクティビティ B にユーザー フォーカスがあります)。
  3. 次に、アクティビティ A が画面に表示されなくなった場合は、onStop() メソッドが実行されます。

このライフサイクル コールバックの順序を予測しておくことで、アクティビティ間の情報の遷移を管理できるようになります。