活動生命週期

當使用者瀏覽、退出及返回應用程式時,應用程式中的 Activity 執行個體會在生命週期中轉換不同的狀態。Activity 類別提供多個回呼,讓活動在狀態變更、系統建立、停止或恢復活動或刪除活動所在的程序時通知活動。

在生命週期回呼方法中,您可以宣告使用者離開並重新進入活動時的行為。舉例來說,如果您正在建構串流影片播放器,則可在使用者切換至其他應用程式時暫停影片並終止網路連線。當使用者返回時,您可以重新連上網路,讓使用者從相同位置繼續播放影片。

每個回呼都可讓您執行適用於特定狀態變更的特定工作。在適當時機完成正確的工作,並適當處理轉場效果,讓應用程式更穩定可靠。舉例來說,良好的生命週期回呼實作有助於應用程式避免以下情形:

  • 如果使用者在使用應用程式時接到來電或切換到其他應用程式,會發生當機情形。
  • 在使用者未主動使用時消耗寶貴的系統資源。
  • 如果使用者離開應用程式,日後再返回,就會失去使用者的進度。
  • 當螢幕在橫向和直向之間切換時,當機或遺失使用者的進度。

本文件將詳細說明活動生命週期。文件一開始先說明生命週期模式。接下來,我們將說明每個回呼:執行時在內部進行的操作,以及回呼期間需要實作的內容。

接著,其中會短暫介紹活動狀態與遭系統終止程序安全漏洞之間的關係。最後,系統會討論與活動狀態之間的轉換相關的幾個主題。

如要瞭解如何處理生命週期,包括最佳做法指南,請參閱「 使用生命週期感知元件處理生命週期」和「儲存 UI 狀態」。如要瞭解如何使用結合架構元件的活動,建構出完善且品質優異的應用程式,請參閱「應用程式架構指南」。

活動生命週期概念

如要在活動生命週期各階段之間導覽轉換,Activity 類別會提供六組核心回呼:onCreate()onStart()onResume()onPause()onStop()onDestroy()。當活動進入新狀態時,系統會叫用每個回呼。

圖 1 以視覺化方式呈現此範例。

圖 1:活動生命週期的簡易說明。

當使用者開始離開活動時,系統會呼叫方法以拆解活動。在某些情況下,活動只會部分移除並保留在記憶體中,例如使用者切換至其他應用程式時。在這種情況下,活動仍可回到前景。

當使用者返回活動時,活動就會從使用者上次離開的地方繼續。少數例外情況下,應用程式無法在背景執行時啟動活動

系統終止特定程序的可能性,以及其中的活動,取決於當下的活動狀態。如要進一步瞭解狀態和排除安全漏洞之間的關係,請參閱「活動狀態以及從記憶體中排除」一節。

視活動的複雜度而定,您可能不需要實作所有生命週期方法。不過,請務必瞭解各種情境,並實作能讓應用程式運作方式符合使用者期望的行為。

生命週期回呼

本節針對活動生命週期中使用的回呼方法提供概念和實作資訊。

部分動作屬於活動生命週期方法。不過,請放置程式碼,以在元件中實作相依元件的動作,而非活動生命週期方法。為此,您必須讓相依元件能夠感知生命週期。如要瞭解如何讓依附元件元件感知生命週期,請參閱「 使用生命週期感知元件處理生命週期」。

onCreate()

您必須實作這個回呼,在系統首次建立活動時觸發。在活動建立時,活動會進入「Created」(建立) 狀態。在 onCreate() 方法中,執行在活動整個生命週期中只執行一次的基本應用程式啟動邏輯。

舉例來說,實作 onCreate() 可能會將資料繫結至清單,將活動與 ViewModel 建立關聯,以及對某些類別範圍變數執行個體化。這個方法會接收 savedInstanceState 參數,這是包含活動先前儲存狀態的 Bundle 物件。如果活動從未存在,Bundle 物件的值為空值。

如果您的生命週期感知元件已掛接至活動生命週期,則會收到 ON_CREATE 事件。系統會呼叫含有 @OnLifecycleEvent 註解的方法,因此生命週期感知元件可以針對建立的狀態執行所需的任何設定程式碼。

以下 onCreate() 方法範例會顯示活動的基本設定,例如:宣告使用者介面 (在 XML 版面配置檔案中定義)、定義成員變數,以及設定部分 UI。在這個範例中,XML 版面配置檔案會將檔案的資源 ID R.layout.main_activity 傳遞至 setContentView()

Kotlin

lateinit var textView: TextView

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

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

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

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

    // Initialize member TextView so it is available later.
    textView = findViewById(R.id.text_view)
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored 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 might 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 superclass onCreate to complete the creation of
    // the activity, like the view hierarchy.
    super.onCreate(savedInstanceState);

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

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

    // Initialize member TextView so it is available later.
    textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored 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 might 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 物件,並在 ViewGroup 中插入新的 View 物件以建構檢視區塊階層。接著,將根層級 ViewGroup 傳遞至 setContentView(),即可使用該版面配置。如要進一步瞭解如何建立使用者介面,請參閱使用者介面說明文件。

您的活動未維持在「已建立」狀態。onCreate() 方法執行完畢後,活動會進入「Started」狀態,而系統會快速呼叫 onStart()onResume() 方法。

onStart()

當活動進入已開始狀態時,系統會叫用 onStart()。此呼叫會在應用程式準備進入前景並產生互動時,向使用者顯示活動。舉例來說,這個方法為維護 UI 的程式碼會初始化。

當活動移至「已開始」狀態時,與活動生命週期繫結的任何生命週期感知元件都會收到 ON_START 事件。

onStart() 方法會快速完成,且與「已建立」狀態一樣,活動不會保持在「啟動」狀態。這個回呼完成後,活動會進入「Resumed」(已重新啟用) 狀態,然後系統會叫用 onResume() 方法。

onResume()

當活動進入已重新啟用狀態時,會進入前景,且系統會叫用 onResume() 回呼。這是應用程式與使用者互動的狀態。應用程式會一直保持此狀態,直到應用程式發生某些事件 (例如接聽電話的裝置、使用者前往其他活動或裝置螢幕關閉) 為止。

當活動移至「Resumed」狀態時,與活動生命週期相關聯的任何生命週期感知元件都會收到 ON_RESUME 事件。在這個階段,生命週期元件可以在元件顯示和前景時,啟用任何需要執行的功能,例如啟動相機預覽。

發生幹擾事件時,活動會進入「Pause」狀態,並叫用 onPause() 回呼。

如果活動從「已暫停」狀態返回已恢復狀態,系統會再次呼叫 onResume() 方法。因此,實作 onResume() 即可初始化在 onPause() 期間發布的元件,並執行活動每次進入重新啟用狀態時必須進行的任何其他初始化作業。

以下範例是生命週期感知元件,在元件收到 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()

系統會呼叫此方法做為第一個指標,表示使用者即將離開活動,但不一定表示活動即將刪除。這表示活動已不在前景,但使用者在多視窗模式下仍會顯示。活動進入此狀態的可能原因如下:

  • 中斷應用程式執行作業的事件 (如 onResume() 回呼所述) 會暫停目前的活動。這是最常見的情況。
  • 在多視窗模式下,一次只能有一個應用程式聚焦,且系統會暫停所有其他應用程式。
  • 開啟新的半透明活動時 (例如對話方塊),會暫停其涵蓋的活動。只要活動部分顯示但未顯示於焦點,活動就會維持暫停狀態。

當活動移至「已暫停」狀態時,與活動生命週期相關聯的任何生命週期感知元件都會收到 ON_PAUSE 事件。此時,生命週期元件可以在元件位於前景時,停止任何不需要執行的功能,例如停止相機預覽。

使用 onPause() 方法,暫停或調整無法繼續或可能繼續進行管理的作業,同時 Activity 處於「已暫停」狀態,而您預期很快就會繼續執行。

您也可以使用 onPause() 方法,在活動暫停期間且使用者不需要時,釋放系統資源、處理感應器 (例如 GPS),或影響電池續航力的任何資源。

不過,如 onResume() 相關章節所述,如果應用程式處於多視窗模式,可能還是能完整顯示已暫停的活動。建議您使用 onStop() 而非 onPause() 全面發布或調整 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() 期間執行的作業,請參閱下一節。如要進一步瞭解如何儲存資料,請參閱「儲存及還原狀態」一節。

完成 onPause() 方法並不代表活動會退出「已暫停」狀態。相反地,活動會一直處於此狀態,直到活動恢復或使用者完全無法看見為止。如果活動繼續執行,系統會再次叫用 onResume() 回呼。

如果活動從「已暫停」狀態返回「已恢復」狀態,系統會將 Activity 例項保留在記憶體中,在系統叫用 onResume() 時召回該例項。在這種情況下,您不需要重新初始化在導致「已恢復」狀態的任何回呼方法中建立的元件。如果活動完全隱藏,系統會呼叫 onStop()

onStop()

當系統不再向使用者顯示活動時,會進入「Stopped」狀態,並叫用 onStop() 回呼。當新啟動的活動佔據整個螢幕時,就可能發生這種情況。此外,在活動執行完畢且即將終止時,系統也會呼叫 onStop()

當活動移至已停止狀態時,與活動生命週期相關聯的任何生命週期感知元件都會收到 ON_STOP 事件。在螢幕未顯示元件時,生命週期元件可以停止任何不需要執行的功能。

onStop() 方法中,請釋出或調整在應用程式未向使用者顯示時不需要的資源。例如,應用程式可能會暫停動畫,或從「精細」切換至概略的位置更新。使用 onStop() 而非 onPause() 表示即使使用者以多視窗模式查看活動,使用者介面相關工作仍會繼續進行。

此外,使用 onStop() 執行相對會耗用大量 CPU 的關閉作業。舉例來說,如果您找不到將資訊儲存到資料庫的最佳時間,則可以在 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。不過,建議您使用 Room,這是透過 SQLite 提供抽象層的持續性程式庫。如要進一步瞭解使用 Room 的好處,以及如何在應用程式中實作 Room,請參閱 Room 持續性程式庫指南。

當活動進入「Stopped」狀態時,Activity 物件會保留在記憶體中:此物件會保留所有狀態和成員資訊,但不會附加至視窗管理員。活動恢復時,系統會記住這項資訊。

您不需要重新初始化在任何回呼方法期間建立的元件,這可能導致「已恢復」狀態。系統也會追蹤版面配置中每個 View 物件的目前狀態,因此如果使用者在 EditText 小工具中輸入文字,系統會保留該內容,因此您不需要儲存及還原。

注意: 如果系統需要復原記憶體,系統可能會在活動停止後刪除包含該活動的程序。即使系統在活動停止時刪除程序,系統仍會保留 View 物件的狀態,例如 EditText 小工具中的文字 Bundle (鍵/值組合的 blob),並在使用者返回活動時還原這些物件。如要進一步瞭解如何將使用者返回的活動還原,請參閱「儲存及還原狀態」一節。

從已停止狀態中,活動會返回與使用者互動,或活動已完成執行並消失。如果活動返回,系統會叫用 onRestart()。如果 Activity 執行完畢,系統會呼叫 onDestroy()

onDestroy()

在刪除活動前,系統會呼叫 onDestroy()。系統叫用這個回呼的原因有兩種:

  1. 由於使用者完全關閉活動,或在活動上呼叫 finish(),因此活動結束。
  2. 由於設定變更 (例如裝置旋轉或進入多視窗模式),系統會暫時刪除活動。

當活動移至已刪除狀態時,與活動生命週期相關聯的任何生命週期感知元件都會收到 ON_DESTROY 事件。這是生命週期元件在刪除 Activity 前清除所需任何內容的位置。

與其在 Activity 中放置邏輯以判斷刪除的原因,請改用 ViewModel 物件來包含 Activity 的相關檢視資料。如果 Activity 因設定變更而重新建立,ViewModel 不必執行任何操作,因為系統會保留這個狀態並提供給下一個 Activity 執行個體。

如果未重新建立 Activity,則 ViewModel 會呼叫 onCleared() 方法,其中可以清除刪除前需要的任何資料。您可以使用 isFinishing() 方法區分這兩種情況。

如果活動已完成,onDestroy() 是活動收到的最終生命週期回呼。如果因設定變更而呼叫 onDestroy(),系統會立即建立新的活動例項,然後在新設定中對該新例項呼叫 onCreate()

onDestroy() 回呼會釋出先前回呼中未釋出的所有資源,例如 onStop()

活動狀態以及從記憶體中排除

系統會在需要釋出 RAM 時終止程序。系統終止特定程序的可能性取決於當時的程序狀態。程序狀態則視處理中的活動狀態而定。表 1 顯示了程序狀態、活動狀態,以及系統終止程序的可能性等關係。只有在程序未執行其他類型的應用程式元件時,才適用這個表格。

被殺的機率 處理狀態 最終活動狀態
極低 前景 (不管有或即將成為焦點) 已重新啟用
顯示 (無焦點) 已開始/已暫停
較高畫質 背景 (隱藏) 已停止
最高金額 空白 已刪除

表 1. 程序生命週期和活動狀態之間的關係。

系統絕不會直接終止活動來釋出記憶體。相反地,它會終止執行活動的程序,而不只是刪除活動,同時也會刪除過程中執行的所有其他項目。如要瞭解如何在系統啟動的程序終止時保留及還原活動的 UI 狀態,請參閱「儲存及還原狀態」一節。

使用者也可以使用應用程式管理員,在「設定」下方終止對應的應用程式。

如要進一步瞭解程序,請參閱「程序和執行緒總覽」。

儲存及還原暫時性 UI 狀態

使用者會預期在設定變更 (例如旋轉或切換至多視窗模式) 期間,活動的 UI 狀態保持不變。不過,當這類設定變更發生時,系統預設會刪除活動,並清除儲存在活動執行個體中的任何 UI 狀態。

同樣地,如果使用者暫時從應用程式切換至其他應用程式,然後再返回應用程式,使用者會預期 UI 狀態將維持不變。不過,當使用者離開且活動停止時,系統可能會刪除應用程式的程序。

系統限制刪除活動時,請使用 ViewModel onSaveInstanceState() 和/或本機儲存空間的組合,保留使用者的暫時性 UI 狀態。如要進一步瞭解與系統行為相比的使用者期望,以及如何在系統啟動的活動和程序終止期間保留複雜的 UI 狀態資料,請參閱「 儲存 UI 狀態」。

本節概述什麼是例項狀態,以及如何實作 onSaveInstance() 方法 (活動本身的回呼)。如果您的 UI 資料輕量,您可以單獨使用 onSaveInstance(),在設定變更和系統啟動程序終止期間保留 UI 狀態。不過,由於 onSaveInstance() 會產生序列化/去序列化的費用,因此在多數情況下,您同時使用 ViewModelonSaveInstance(),如「 儲存 UI 狀態」一文所述。

注意: 如要進一步瞭解設定變更、如何視需要限制活動重建,以及如何回應 View 系統和 Jetpack Compose 中的設定變更,請參閱「處理設定變更」頁面。

執行個體狀態

在某些情況下,活動會因為正常的應用程式行為而遭到刪除,例如使用者按下「返回」按鈕,或您的活動透過呼叫 finish() 方法自行訊號刪除。

當使用者按下返回或活動本身完成刪除時,系統和使用者 對於該 Activity 執行個體的概念就會遭到刪除。在這類情境中,使用者的期望會與系統行為相符,且您不需要執行任何額外作業。

然而,如果系統因系統限制 (例如設定變更或記憶體壓力) 而刪除活動,雖然實際的 Activity 執行個體已不存在,但系統會記住該活動已存在。如果使用者嘗試返回活動,系統會使用一組已儲存的資料建立新的活動例項,該例項用於描述刪除活動時的活動狀態。

系統用來還原先前狀態的已儲存資料稱為「執行個體狀態」。這是儲存在 Bundle 物件中的一組鍵/值組合。根據預設,系統會使用 Bundle 例項狀態,儲存活動版面配置中每個 View 物件的相關資訊,例如在 EditText 小工具中輸入的文字值。

因此,如果活動執行個體遭到刪除並重新建立,則版面配置的狀態會還原為先前的狀態,您無需任何程式碼。不過,活動可能會有更多需要還原的狀態資訊,例如會追蹤使用者活動進度的成員變數。

注意: 為了讓 Android 系統還原活動中的檢視畫面狀態,每個檢視畫面都必須具有由 android:id 屬性提供的專屬 ID。

Bundle 物件需要在主執行緒上進行序列化並消耗系統程序記憶體,因此不適合保留這麼多的資料,因此不適合保留太多資料。如要保留超過極少量的資料,請採用合併方法保留資料,包括使用永久本機儲存空間、onSaveInstanceState() 方法和 ViewModel 類別,如「 儲存 UI 狀態」一文所述。

使用 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() 方法,因此會呼叫系統是否建立新的活動執行個體或重新建立先前的活動,因此您必須先檢查狀態 Bundle 是否為空值,然後再嘗試讀取。若為空值,系統會建立新的活動執行個體,而不是還原已刪除的前一個執行個體。

下列程式碼片段說明如何還原 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.
    }
    // ...
}

您可以選擇實作 onRestoreInstanceState(),讓系統在執行 onStart() 方法後呼叫此內容,而非在 onCreate() 期間還原狀態。只有在有要還原的已儲存狀態時,系統才會呼叫 onRestoreInstanceState(),因此您不需要檢查 Bundle 是否為空值。

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 額外項目是電子郵件要接收電子郵件的字串陣列。當電子郵件應用程式回應此意圖時,會讀取額外項目中提供的字串陣列,並將地址放在電子郵件撰寫表單的「to」欄位中。在這種情況下,電子郵件應用程式的活動會開始,使用者操作完成後,活動就會繼續執行。

startActivityForResult()

有時候,您想在活動結束時取得結果。例如,您可以啟動一個活動,讓使用者在聯絡人清單中挑選某人。結束之後,系統會傳回所選人員。如要這麼做,請呼叫 startActivityForResult(Intent, int) 方法,其中整數參數可用來識別呼叫。

此 ID 旨在區分對 startActivityForResult(Intent, int) 的多次呼叫與同一活動。這不是全域 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) {
                    // A contact was picked. Display it to the user.
                    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. Display it to the user.
                 startActivity(new Intent(Intent.ACTION_VIEW, data));
             }
         }
     }
 }

協調活動

當一項活動啟動另一個活動時,兩個活動都會經歷生命週期轉換。建立另一個活動時,第一個活動會停止運作並進入「已暫停」或「已停止」狀態。如果這些活動共用儲存在光碟或其他地方的資料,您務必要瞭解第一個活動在建立第二個活動之前不會完全停止。而是與停止第一個動作的程序重疊。

生命週期回呼的順序明確定義,特別是當兩個活動具有相同的程序 (也就是相同應用程式) 且其中一個活動開始另一個活動時。以下是活動 A 啟動活動 B 時發生的作業順序:

  1. 系統會執行活動 A 的 onPause() 方法。
  2. 活動 B 的 onCreate()onStart()onResume() 方法會依序執行。活動 B 現在有使用者焦點。
  3. 如果螢幕上不再顯示活動 A,系統會執行其 onStop() 方法。

這個生命週期回呼順序可讓您管理不同活動之間的資訊轉換情形。