The Android Developer Challenge is back! Submit your idea before December 2.

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

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

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

  • アプリの使用中にユーザーが通話を受けたり、別のアプリに切り替えたりするとクラッシュする。
  • ユーザーの使用頻度が低いときに貴重なシステムリソースを消費する。
  • ユーザーがアプリから離れ、後で復帰した場合に、アプリを離れた時点の作業状態が失われる。
  • 画面の向きが横向きと縦向きとの間で切り替わったときに、クラッシュしたり、ユーザーの作業内容が失われたりする。

このドキュメントでは、アクティビティのライフサイクルについて詳しく説明します。最初に、ライフサイクルの流れについて説明します。次に、各コールバックについて、コールバックの実行中に内部で行われる処理と、コールバックの実行中に何を実装すべきかを説明します。その後、アクティビティの状態と、システムにより強制終了されるプロセスの脆弱性の関係について説明します。最後に、アクティビティの状態の遷移に関連するいくつかのトピックについて説明します。

ベストプラクティスのガイダンスなど、ライフサイクルの処理については、ライフサイクル対応コンポーネントによるライフサイクルの処理およびUI の状態を保存するをご覧ください。アーキテクチャ コンポーネントとアクティビティを組み合わせて利用してプロダクト レベル品質の堅牢なアプリを設計する方法については、アプリ アーキテクチャ ガイドをご覧ください。

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

アクティビティのライフサイクルの各段階を遷移する移動のために、アクティビティ クラスにより 6 つのコールバックのコアセットが提供されます。コールバックは、onCreate()onStart()onResume()onPause()onStop()onDestroy() です。アクティビティが新しい状態になると、システムによりこれらの各コールバックが呼び出されます。

図 1 は、この流れを図示しています。

図 1. アクティビティのライフサイクルの簡略図。

ユーザーがアクティビティを離れる操作を開始すると、システムはアクティビティを破棄するためのメソッドを呼び出します。この破棄は、部分的に行われる場合もあります。アクティビティが引き続きメモリに残り(ユーザーが別のアプリに切り替える場合など)、後でそのアクティビティがフォアグラウンドに戻ることがあります。ユーザーがそのアクティビティに戻ると、ユーザーが離れた位置からアクティビティが再開されます。いくつかの例外を除き、アプリはバックグラウンドで実行中にアクティビティを開始することはできません

システムが特定のプロセスと - およびプロセスの中に含まれているアクティビティ - を強制終了する可能性は、その時点でのアクティビティの状態によって異なります。アクティビティの状態とメモリからのイジェクトに、状態とイジェクションに対する脆弱性との間の関係について詳しい説明があります。

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

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

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

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

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

onCreate()

このコールバック メソッドを実装する必要があります。これは、システムが初めてアクティビティを作成するときに発生するコールバックです。アクティビティの作成時に、アクティビティは作成状態になります。onCreate() メソッドでは、アクティビティの存続期間にわたり一度だけ発生する必要がある基本的なアプリケーション起動ロジックを実行します。たとえば onCreate() の実装では、データがリストにバインドされ、アクティビティが ViewModel に関連付けられ、いくつかのクラススコープの変数がインスタンス化されます。このメソッドはパラメータ savedInstanceState を受け取ります。このパラメータは、以前に保存されたアクティビティの状態が含まれている Bundle オブジェクトです。これまでにこのアクティビティが存在していなかった場合は、Bundle オブジェクトの値は null です。

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

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

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 レベルl 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;
        }
    }

    ...
}

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

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 が何らかの処理を行う必要はありません。アクティビティが再作成されない場合は、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);

アクティビティからのデータを使用して、アプリケーションでメールやテキスト メッセージの送信、ステータスのアップデートといった操作を実行する場合もあります。アプリケーションにそのような操作を実行できるアクティビティがない場合、代わりに、端末上の他のアプリケーションによるアクティビティを活用できます。ここが、インテントがその存在意義を発揮する場面です。実行する操作を記述するインテントを作成すると、システムが適切なアクティビティを他のアプリケーションから起動します。インテントを処理できるアクティビティが複数ある場合は、使用するアクティビティを 1 つユーザーが選択できます。たとえば、メールを送信できるようにする場合は、次のようなインテントを作成します。

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 ではなく、他のアプリやアクティビティと競合するリスクはありません。結果は onActivityResult(int, int, Intent) メソッドにより返されます。

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

クラッシュなど何らかの原因で子アクティビティが失敗すると、親アクティビティはコード 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 つのアクティビティで別のアクティビティを開始すると、双方でライフサイクルの遷移が生じます。最初のアクティビティが動作を停止して一時停止状態または停止状態になり、もう 1 つのアクティビティが作成されます。これらのアクティビティでディスクなどに保存されているデータを共有している場合は、2 つ目のアクティビティが作成される前に 1 つ目のアクティビティが完全に停止することはないということを理解しておくことが重要です。むしろ、2 つ目の開始プロセスは、1 つ目の停止プロセスとオーバーラップします。

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

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

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