Android Kotlin 基本概念 04.2:複雜的生命週期情況

本程式碼研究室是 Android Kotlin 基礎課程的一部分。使用程式碼研究室逐步完成程式碼課程後,您將能充分發揮本課程的潛能。所有課程程式碼研究室清單均列於 Android Kotlin 基礎程式碼研究室到達網頁

簡介

在上一次的程式碼研究室中,您已瞭解 ActivityFragment 的生命週期,並探討了在活動及片段的生命週期狀態改變時,所呼叫的方法。在這個程式碼研究室中,您將進一步探索活動生命週期。您也會瞭解 Android Jetpack 的生命週期程式庫,其中的程式碼可用於妥善整理,且更容易維護,方便您管理生命週期事件。

須知事項

  • 何謂活動,以及如何在應用程式中建立活動。
  • ActivityFragment 生命週期的基本資訊,以及活動在狀態之間移動時叫用的回呼。
  • 如何覆寫 onCreate()onStop() 生命週期回呼方法,以便在活動或片段生命週期中在不同時間執行作業。

您將會瞭解的內容

  • 如何在生命週期回呼中設定、啟動及停止應用程式的部分內容。
  • 如何使用 Android 生命週期程式庫建立生命週期觀察器,並簡化活動和片段生命週期。
  • Android 處理程序關閉對應用程式中的資料有何影響,以及如何在 Android 關閉應用程式時自動儲存和還原這些資料。
  • 裝置旋轉與其他設定的變更會改變生命週期狀態,並影響應用程式的狀態。

要執行的步驟

  • 修改 DessertClicker 應用程式以加入計時器功能,並在活動生命週期中的各個時間開始和停止計時器。
  • 修改應用程式以使用 Android 生命週期程式庫,並將 DessertTimer 類別轉換為生命週期觀測器。
  • 設定並使用 Android Debug Bridge (adb) 來模擬應用程式的處理程序關閉情形,以及之後發生的生命週期回呼。
  • 導入 onSaveInstanceState() 方法,保留未預期關閉的應用程式可能會遺失的應用程式資料。新增程式碼,以在應用程式再次啟動時還原該資料。

在本程式碼研究室中,您將從先前的程式碼研究室展開 DessertClicker 應用程式。您新增背景計時器,然後將應用程式轉換為使用 Android 生命週期程式庫

在先前的程式碼研究室中,您已學會覆寫各種生命週期回呼,並在系統叫用這些回呼時記錄,藉此觀察活動及片段的生命週期。在這項工作中,您會探索到在 DessertClicker 應用程式中管理生命週期工作的更複雜的範例。您使用計時器,每秒列印記錄陳述式,並包含執行記錄的秒數。

步驟 1:設定 DessertTimer

  1. 開啟最後一個程式碼研究室的 DessertClicker 應用程式。(如果您沒有這個應用程式,可以在這裡下載 DessertClickerLogs)。
  2. 在「專案」檢視畫面中,展開 java > com.example.android.dessertclicker,然後開啟 DessertTimer.kt。請注意,所有的程式碼都已加上註解,因此並非在應用程式中執行。
  3. 選取編輯器視窗中的所有程式碼。選取 [程式碼 &註解 (含行註解)],或按下 Control+/ 鍵 (在 Mac 系統上為 Command+/)。這個指令會將檔案中的所有程式碼取消註解。(在您重新建立應用程式前,Android Studio 可能會顯示未解決的參照錯誤)。
  4. 請注意,DessertTimer 類別包含 startTimer()stopTimer(),用於啟動和停止計時器。執行 startTimer() 時,計時器會每秒列印一份記錄訊息,同時顯示執行時間的秒數。stopTimer() 方法則會停止計時器和記錄陳述式。
  1. 開啟 MainActivity.kt。在類別頂端的 dessertsSold 變數下方,為計時器新增變數:
private lateinit var dessertTimer : DessertTimer;
  1. 向下捲動到 onCreate() 並建立新的 DessertTimer 物件,就在呼叫 setOnClickListener() 之後:
dessertTimer = DessertTimer()


現在你已擁有甜點計時器物件,請考慮應該從何時開始及停止計時器,這樣活動才只在螢幕上顯示時執行。在接下來的步驟中,您將看到幾個選項。

步驟 2:啟動和停止計時器

系統會在活動顯示之前呼叫 onStart() 方法。活動停止顯示後,系統會呼叫 onStop() 方法。這些回呼似乎適合啟動及停止計時器的時間。

  1. MainActivity 類別中,啟動 onStart() 回呼中的計時器:
override fun onStart() {
   super.onStart()
   dessertTimer.startTimer()

   Timber.i("onStart called")
}
  1. 停止計時 onStop()
override fun onStop() {
   super.onStop()
   dessertTimer.stopTimer()

   Timber.i("onStop Called")
}
  1. 編譯並執行應用程式。在 Android Studio 中按一下「Logcat」窗格。在 Logcat 搜尋框中輸入 dessertclicker,這樣就能依 MainActivityDessertTimer 類別進行篩選。請注意,應用程式啟動後,計時器也會立即開始計時。
  2. 按一下 [返回] 按鈕,然後記下計時器停止計時。計時器和所控制的計時器已遭刪除,因此計時器已停止倒數。
  3. 使用「最近」畫面即可返回應用程式。請注意 Log Logcat 的計時器是從 0 開始。
  4. 按一下 [分享] 按鈕。請在 Logcat 中記下計時器仍在倒數中。

  5. 按一下 [首頁] 按鈕。在 Logcat 中,您會看到計時器已停止計時。
  6. 使用「最近」畫面即可返回應用程式。請注意,Logcat 會記錄計時器從上次停止的地方重新開機。
  7. MainActivityonStop() 方法中,為 stopTimer() 加上註解。註解 stopTimer() 會顯示您已在 onStart() 開始的作業,但別忘了在 onStop() 中停止執行該作業。
  8. 編譯並執行應用程式,並在計時器啟動後按一下主螢幕按鈕。即使應用程式在背景執行,計時器仍會執行,並持續使用系統資源。讓計時器繼續運作時,應用程式可能會佔用記憶體,可能不是你想要的行為。

    一般來說,當你在回呼中設定或啟動某個項目,代表停止或移除該對應回呼中的項目。如此一來,您就不會發現任何不再需要的項目。
  1. 在「onStop()」中,將你停止計時器的時間取消註解。
  2. 剪下 startTimer() 通話,從 onStart() 貼到 onCreate()。本次異動表示,您是在 onCreate() 中初始化及啟動資源,而非使用 onCreate() 初始化資源,而是使用 onStart() 啟動資源。
  3. 編譯並執行應用程式。請注意,計時器會開始倒數時間。
  4. 按一下 [首頁] 即可停止應用程式;計時器會停止正常運作,
  5. 使用「最近」畫面即可返回應用程式。請注意,在這種情況下,計時器「不會」重新啟動,因為系統只會在應用程式啟動時呼叫 onCreate(),也就是應用程式回到前景時不會呼叫。

重要注意事項:

  • 在生命週期回呼中設定資源時,請一併將資源拆解。
  • 請依照對應的方法進行設定和拆解。
  • 如果你已在onStart()中設定任何項目,請前往 onStop() 停止或關閉。

在 DessertClicker 應用程式中,如果在 onStart() 中啟動了計時器,則需要在 onStop() 停止計時器。這只有一個計時器,所以停止計時器並不容易記住。

若是比較複雜的 Android 應用程式,您可以在 onStart()onCreate() 中設定許多項目,然後在 onStop()onDestroy() 中全部完成拆解。舉例來說,您可能會需要設定及拆解動畫、音樂、感應器或計時器,並且設定開始和停止的時間。如果忘記其中的資料,就會造成錯誤和頭痛。

生命週期程式庫屬於 Android Jetpack 的一部分,因此可以簡化這項工作。當您需要追蹤許多移動零件時,該程式庫特別有用,其中有些是處於不同的生命週期狀態。程式庫會按照生命週期執行方式:通常活動或片段會告訴元件 (例如 DessertTimer) 在發生生命週期回呼時該如何處理。但當您使用生命週期程式庫時,元件本身會觀察生命週期變化,並在變更發生時執行必要的操作。

生命週期程式庫主要分為三個部分:

  • 生命週期擁有者是指具有 (也就是「本身」) 生命週期的元件。ActivityFragment 是生命週期擁有者。生命週期擁有者會導入 LifecycleOwner 介面。
  • Lifecycle 類別,該類別包含生命週期擁有者的實際狀態,並在生命週期變更發生時觸發事件。
  • 生命週期觀測器:可觀察生命週期狀態,並在生命週期變更時執行工作。生命週期觀察器實作 LifecycleObserver 介面。

在這項工作中,您將轉換 DessertClicker 應用程式以使用 Android 生命週期程式庫,並瞭解這個程式庫如何簡化 Android 活動和片段生命週期的管理。

步驟 1:將 DessertTimer 轉換為 LifecycleObserver

生命週期程式庫的主要部分是生命週期觀察的概念。「觀察項目」可讓類別 (例如 DessertTimer) 瞭解活動或片段的生命週期,並根據這些生命週期狀態的變更開始及停止執行本身作業。有了生命週期觀察器,您就可以移除啟動和停止活動和片段方法的物件。

  1. 開啟 DesertTimer.kt 類別。
  2. 變更 DessertTimer 類別的簽名,如下所示:
class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {

這項新類別定義包含兩個功能:

  • 建構函式會採用 Lifecycle 物件,也就是計時器觀察的生命週期。
  • 類別定義會實作 LifecycleObserver 介面。
  1. runnable 變數下方,將 init 區塊新增至類別定義。在 init 區塊中,使用 addObserver() 方法將從擁有者 (活動) 傳入的生命週期物件連結到這個類別 (觀察器)。
 init {
   lifecycle.addObserver(this)
}
  1. 使用 @OnLifecycleEvent annotationstartTimer() 加上註解,並使用 ON_START 生命週期事件。生命週期觀察員可觀察的所有生命週期事件都屬於 Lifecycle.Event 類別。
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {
  1. stopTimer() 執行相同的動作,使用 ON_STOP 事件:
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer()

步驟 2:修改 MainActivity

您的 MainActivity 類別已成為繼承的生命週期擁有者,因為 FragmentActivity 父類別實作了 LifecycleOwner。因此,您不需要採取任何行動,就能讓活動更清晰易懂。您只需要將活動生命週期物件傳送到 DessertTimer 建構函式即可。

  1. 開啟 MainActivity。在 onCreate() 方法中,修改 DessertTimer 的初始化設定以納入 this.lifecycle
dessertTimer = DessertTimer(this.lifecycle)

活動的 lifecycle 屬性包含這個活動擁有的 Lifecycle 物件。

  1. 移除onCreate()startTimer() 通話,以及 onStop() 中的 stopTimer() 通話。你不必告訴DessertTimer該如何處理該活動,因為「DessertTimer」現在觀測到生命週期,並在生命週期狀態改變時自動收到通知。您在這些回呼中所做的一切,就是記錄訊息。
  2. 編譯並執行應用程式,然後開啟 Logcat。請注意,計時器已經開始正常執行。
  3. 按一下主螢幕按鈕即可將應用程式置於背景。請注意,計時器已停止運作。

如果 Android 在背景中關閉該應用程式,您的應用程式及其相關資料會有什麼影響?這個棘手的保護殼必須能夠理解。

您的應用程式進入背景後,不會遭到銷毀,只會停止並等候使用者返回。然而,Android OS 的主要考量之一,就是讓前景活動不中斷。例如,如果使用者使用 GPS 應用程式來協助搭公車,請務必快速顯示該 GPS 應用程式,並持續顯示路線。但保留 DessertClicker 應用程式並不重要,因為使用者可能沒有在幾天後都檢視了應用程式,因此該應用程式在背景執行順暢。

Android 會限制背景應用程式,讓前景應用程式能夠順利執行。舉例來說,Android 對背景應用程式可執行的處理量設有限制。

有時候,Android 甚至會關閉整個應用程式處理程序,包括與應用程式相關的所有活動。Android 會在系統受到壓力且有視覺延遲時進行這類關閉,因此目前並未執行額外的回呼或程式碼。應用程式處理程序只是在背景中自動關閉。但對使用者而言,應用程式似乎沒有關閉。當使用者返回 Android 作業系統關閉的應用程式時,Android 會重新啟動該應用程式。

在這項工作中,您將模擬 Android 處理程序關閉,然後查看應用程式重新啟動時會發生什麼情況。

步驟 1:使用 ADB 模擬關閉程序

Android Debug Bridge (adb) 這項指令列工具可讓您將操作說明傳送至連接至電腦的模擬器和裝置。在這個步驟中,您會使用 adb 關閉應用程式處理程序,並瞭解 Android 關閉應用程式時會發生什麼情況。

  1. 編譯並執行您的應用程式。按一下杯子蛋糕數次。
  2. 按下主螢幕按鈕,讓應用程式在背景執行。您的應用程式已停止;如果 Android 需要應用程式使用的資源,應用程式可能會遭到關閉。
  3. 在 Android Studio 中,按一下 [Terminal] 標籤以開啟指令列終端機。
  4. 輸入「adb」並按下 Return 鍵。

    如果你看到許多輸出內容以「Android Debug Bridge version X.XX.X」開頭,並以 tags to be used by logcat (see logcat —help 結尾,就表示一切正常。如果您看到 adb: command not found,請確認執行路徑中有 adb 指令。如需操作說明,請參閱實用工具一章中的「將 Ab 新增到執行路徑」一節。
  5. 請複製這則註解並貼到指令列,然後按下 Return 鍵:
adb shell am kill com.example.android.dessertclicker

這個指令會告知所有已連結的裝置或模擬器停止使用 dessertclicker 套件名稱執行處理程序,但前提是應用程式是在背景執行。您的應用程式是在背景執行,裝置或模擬器畫面不會顯示程序已經停止。在 Android Studio 中,按一下 [Run] (執行) 分頁標籤來查看「App 已終止」的訊息。按一下 [Logcat] 分頁標籤,查看系統從未執行 onDestroy() 回呼,因為您的活動已經結束。

  1. 使用「最近」畫面即可返回應用程式。無論是在背景執行操作 (或是已完全停止),應用程式都會出現在「最近」清單中。當您使用 [最近] 畫面返回應用程式時,就會再次啟動該活動。這項活動會經過一組啟動生命週期回呼,包括 onCreate()
  2. 請注意,應用程式重新啟動時,會重設你的「分數」(包含甜點數和總盈餘) 重設為預設值 (0)。如果 Android 關閉了您的應用程式,為什麼無法儲存您所在的狀態?

    當 Android 重新啟動應用程式後,Android 會嘗試嘗試將應用程式重設為先前的狀態,當您離開活動時,Android 會儲存部分檢視狀態,並將它儲存到組合中。某些是自動儲存的資料,例如 EditText 中的文字 (前提是他們在版面配置中設定了 ID),以及活動的後堆疊。

    不過,Android OS 有時會不瞭解你所有資料。舉例來說,如果您的 DessertClicker 應用程式中有 revenue 這類自訂變數,Android OS 就無法知道這類資料或對活動的重要性。您必須自行將這些資料加入組合中。

步驟 2:使用 onSaveInstanceState() 儲存套件資料

onSaveInstanceState() 方法是您用來儲存 Android 作業系統中,用於儲存應用程式可能需要的所有資料。在生命週期停止圖表中,系統會在活動停止後呼叫 onSaveInstanceState()。每次應用程式進入背景時,都會呼叫。

您可以將 onSaveInstanceState() 呼叫視為安全防護措施;此呼叫會在活動離開前景時,將少量資訊儲存至套件。系統現在會儲存這些資料,因為在等待應用程式關閉之前,作業系統可能處於資源壓力。每次儲存資料可確保資料組合中的更新資料可在需要時復原。

  1. MainActivity 中,覆寫 onSaveInstanceState() 回呼,然後新增 Timber 記錄陳述式。
override fun onSaveInstanceState(outState: Bundle) {
   super.onSaveInstanceState(outState)

   Timber.i("onSaveInstanceState Called")
}
  1. 編譯並執行應用程式,然後按一下「主畫面」按鈕,讓應用程式進入背景。請注意,onSaveInstanceState() 回呼會在 onPause()onStop() 之後發生:
  2. 在檔案頂端,在類別定義之前,新增以下常數:
const val KEY_REVENUE = "revenue_key"
const val KEY_DESSERT_SOLD = "dessert_sold_key"
const val KEY_TIMER_SECONDS = "timer_seconds_key"

您將使用這些鍵儲存與擷取執行個體狀態套件中的資料。

  1. 向下捲動至「onSaveInstanceState()」,然後查看「Bundle」類型的「outState」參數。

    「組合」是一組鍵/值組合,其中的鍵一律為字串。您可以在組合中加入原始值 (例如 intboolean 值)。
    由於這個套件是將 RAM 組合保留在 RAM 中,所以最佳做法是盡量避免資料包中的資料。此套件的大小有限,但尺寸會因裝置而異。一般情況下,儲存的儲存空間應小於 10 萬,否則可能會發生TransactionTooLargeException錯誤,導致應用程式當機。
  2. onSaveInstanceState() 中使用 putInt() 方法,將 revenue 值 (整數) 放到組合中:
outState.putInt(KEY_REVENUE, revenue)

putInt() 方法 (以及來自 Bundle 類別的類似方法,例如 putFloat()putString()) 使用兩個引數:鍵的字串 (KEY_REVENUE 常數),以及要儲存的實際值。

  1. 以銷售的甜點數量和計時器狀態重複相同的程序:
outState.putInt(KEY_DESSERT_SOLD, dessertsSold)
outState.putInt(KEY_TIMER_SECONDS, dessertTimer.secondsCount)

步驟 3:使用 onCreate() 還原組合資料

  1. 請捲動至 onCreate(),並檢查方法簽名:
override fun onCreate(savedInstanceState: Bundle) {

請注意,每次呼叫時,onCreate() 都會獲得一個 Bundle。活動因關機而重新啟動時,系統會將你儲存的組合傳送至「onCreate()」。如果你的活動剛開始時,onCreate() 中的套裝組合就是 null。因此,如果套裝組合並非 null,就表示您知道「重新建立活動」。

  1. DessertTimeronCreate() 之後,將這段程式碼加進:
if (savedInstanceState != null) {
   revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}

null 的測試可判斷套件中是否含有資料,或套件是否為 null,以讓您瞭解應用程式是否已重新啟動,或在關閉後重新建立。此測試是還原組合中常見的模式。

請注意,您在此處使用的鍵 (KEY_REVENUE) 與您在 putInt() 中使用的鍵相同。為確保每次都使用相同的鍵,最佳做法是將這些鍵定義為常數。請使用 getInt() 將資料移出套件,如同使用 putInt() 將資料放入套件一樣。getInt() 方法會使用兩個引數:

  • 可做為鍵的字串,例如收益值的 "key_revenue"
  • 預設值,假如套件中沒有該鍵的任何值。

系統會將從套件中取得的整數指派給 revenue 變數,且 UI 會使用該值。

  1. 新增 getInt() 方法來還原售出的甜點數量和計時器的價值:
if (savedInstanceState != null) {
   revenue = savedInstanceState.getInt(KEY_REVENUE, 0)dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
   dessertTimer.secondsCount =
       savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
}
  1. 編譯並執行應用程式。只要按下杯子蛋糕至少五次,直到切換到甜甜圈為止。按一下 [首頁] 即可將應用程式置於背景。
  2. 在 Android Studio 的「Terminal」分頁中執行 adb,關閉應用程式的處理程序。
adb shell am kill com.example.android.dessertclicker
  1. 使用「最近」畫面返回應用程式。請注意,這次應用程式傳回正確的收益,並販售套裝組合中售出的銷售額。同時也注意到甜點已經回到杯子蛋糕。還有一個步驟,可以確保應用程式完全從關機的方式傳回。
  2. MainActivity 中檢查 showCurrentDessert() 方法。請注意,這種方法會根據目前銷售的甜點數,以及 allDesserts 變數中的甜點清單,決定要在活動中顯示哪些甜點圖片。
for (dessert in allDesserts) {
   if (dessertsSold >= dessert.startProductionAmount) {
       newDessert = dessert
   }
    else break
}

此方法仰賴甜點的售出數量來選擇正確的圖片。因此,您無須採取任何行動,以儲存 onSaveInstanceState() 中套件的圖片參照。您已在該套件中儲存甜點售出數量。

  1. onCreate() 中,從還原組合中的狀態還原區塊中,呼叫 showCurrentDessert()
 if (savedInstanceState != null) {
   revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
   dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
   dessertTimer.secondsCount = 
      savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
   showCurrentDessert()                   
}
  1. 編譯並執行應用程式,然後在背景中執行。使用 adb 關閉程序。使用「最近」畫面即可返回應用程式。請注意,聲明的甜點金額、總收益和甜點圖片均已修正成功。

在管理活動和片段生命週期方面,這是最後一項特別值得注意的特殊案例:設定變更對活動和片段的生命週期有何影響。

設定變更於裝置狀態改變時發生,因此基本上,使系統解決變更的最簡單方法就是完全關閉,然後重新建立活動。舉例來說,如果使用者變更了裝置語言,整個版面配置就必須隨之調整,以配合不同的文字方向。如果使用者將裝置插入座架或新增實體鍵盤,應用程式版面配置可能必須採用不同的顯示大小或版面配置。如果裝置方向改變 (如果裝置從縱向轉成橫向或相反方向),版面配置可能就會改變,以配合新的螢幕方向。

步驟 1:探索裝置旋轉及生命週期回呼

  1. 編譯並執行您的應用程式,然後開啟 Logcat。
  2. 將裝置或模擬器旋轉至橫向模式。您可以使用旋轉按鈕向左或向右旋轉模擬器,也可以使用 Control 和方向鍵 (Mac 上的Command 和方向鍵) 來切換模擬器。
  3. 檢查 Logcat 中的輸出內容。篩選 MainActivity 上的輸出。
    請注意,當裝置或模擬器旋轉螢幕時,系統會呼叫所有生命週期回呼來關閉活動。接著,在重新建立活動時,系統會呼叫所有生命週期回呼以啟動活動。
  4. MainActivity 中加註整個 onSaveInstanceState() 方法。
  5. 編譯並再次執行應用程式。按一下杯子蛋糕數次,然後旋轉裝置或模擬器。這時,裝置經過旋轉且活動關閉並重新建立後,活動就會從預設值開始。

    在設定變更後,Android 會使用你在前一項工作中發現的相同執行個體狀態組合,儲存和還原應用程式的狀態。就像處理處理程序一樣,請使用 onSaveInstanceState() 將應用程式的資料納入組合中。然後還原 onCreate() 中的資料,以免裝置旋轉時遺失活動狀態資料。
  6. MainActivity 中取消 onSaveInstanceState() 方法的註解,執行應用程式,按一下杯子蛋糕,然後旋轉應用程式或裝置。請注意,系統每次進行活動輪播時,都會保留沙漠資料。

Android Studio 專案:DessertClickerFinal

生命週期提示

  • 如果您在生命週期回呼中設定或開始執行特定作業,請停止或移除對應的回呼。將內容停止,即可確定在不再需要時也不會繼續運作。舉例來說,如果您在 onStart() 設定了計時器,就必須在 onStop() 中暫停或停止計時器。
  • 只有在應用程式第一次啟動時,才使用 onCreate() 來初始化應用程式一次執行的部分。使用 onStart() 即可啟動應用程式的啟動作業,以及應用程式啟動至前景時分別執行的時間。

生命週期資料庫

  • 使用 Android 生命週期程式庫,將生命週期控制項從活動或片段移到需要生命週期評估的實際元件。
  • 生命週期擁有者是指具有 (且因此是「自身」) 生命週期 (包括 ActivityFragment) 的元件。生命週期擁有者會導入 LifecycleOwner 介面。
  • 生命週期伺服器會關注目前的生命週期狀態,並在生命週期變更時執行工作。生命週期觀察器實作 LifecycleObserver 介面。
  • Lifecycle 物件包含實際的生命週期狀態,並在生命週期變更時觸發事件。

如要建立生命週期感知類別:

  • 在必須認知生命週期階段的類別中使用 LifecycleObserver 介面。
  • 使用活動或片段中的生命週期物件初始化生命週期觀察器類別。
  • 在生命週期觀測器類別中,為生命週期評估方法加上自己感興趣的生命週期狀態註解。

    例如,@OnLifecycleEvent(Lifecycle.Event.ON_START) 註解表示該方法正在觀看 onStart 生命週期事件。

處理程序關閉及儲存活動狀態

  • Android 會規範應用程式在背景執行,讓前景應用程式能夠順利執行。這項規定包括限制應用程式在背景執行的作業量,甚至是關閉整個應用程式處理程序。
  • 使用者無法得知系統是否已在背景關閉應用程式。應用程式仍會顯示在最近的畫面中,應以使用者離開應用程式的狀態重新啟動。
  • Android Debug Bridge (adb) 這項指令列工具可讓您將操作說明傳送至連接至電腦的模擬器和裝置。您可以使用 adb 來模擬應用程式的處理程序關閉情形。
  • 當 Android 關閉您的應用程式處理程序時,不會呼叫 onDestroy() 生命週期方法,應用程式會停止。

保留活動和片段狀態

  • 應用程式進入背景後,在呼叫 onStop() 後,應用程式資料就會儲存到軟體包中。系統會自動儲存部分應用程式資料 (例如 EditText 的內容)。
  • 組合是 Bundle 的執行個體,這是鍵和值的集合。這些鍵一律為字串。
  • 使用 onSaveInstanceState() 回呼將其他資料儲存至您要保留的套件 (即使應用程式會自動關閉)。如要將資料放入組合中,請使用開頭為 put 的組合方法,例如 putInt()
  • 您可以從 onRestoreInstanceState() 方法中的套件,或更常見的 onCreate() 取回資料。onCreate() 方法的 savedInstanceState 參數可用來保存套件。
  • 如果 savedInstanceState 變數包含 null,則表示此活動是在沒有狀態組合的情況下啟動,而且沒有可擷取的狀態資料。
  • 如要透過鍵從套件擷取資料,請使用以 get 開頭的 Bundle 方法,例如 getInt()

設定變更

  • 「設定變更」是指裝置的狀態變更時,為了順利解決問題,最簡單的方式就是關閉並重新建立活動。
  • 最常見的設定變更範例為,使用者將裝置從直向轉為橫向模式,或從橫向轉為直向模式時。裝置語言變更或硬體鍵盤已接上電源時,您也可以變更設定。
  • 發生設定變更時,Android 會叫用所有活動生命週期的關閉回呼。接著,Android 會從頭重新啟動活動,並執行所有生命週期啟動回呼。
  • 當 Android 因設定變更而關閉應用程式時,就會使用可設為 onCreate() 狀態的套件來重新啟動活動。
  • 和處理程序關閉時一樣,請將應用程式的狀態儲存至 onSaveInstanceState() 中的軟體包。

Udacity 課程:

Android 開發人員說明文件:

其他:

這個部分會列出在代碼研究室中,受老師主導的課程作業的可能學生作業。由老師自行決定要執行下列動作:

  • 視需要指派家庭作業。
  • 告知學生如何提交家庭作業。
  • 批改家庭作業。

老師可視需要使用這些建議,並視情況指派其他合適的家庭作業。

如果您是自行操作本程式碼研究室,歡迎透過這些家庭作業來測試自己的知識。

變更應用程式

開啟第 1 課的 DiceRoller 應用程式。(如果您沒有這個應用程式,可以在這裡下載)。編譯並執行應用程式。請注意,如果您旋轉裝置,就會失去骰子的目前值。導入 onSaveInstanceState() 即可將該值保留在組合中,並在 onCreate() 中還原該值。

回答這些問題

第 1 題

您的應用程式含有需要進行大量運算才能呈現的物理模擬。接著,使用者接聽了一通來電。下列何者敘述正確?

  • 在通話期間,您應持續計算物件模擬在物理模擬中的位置。
  • 在通話期間,您應該停止計算物理模擬中的物件位置。

第 2 題

若要暫停已不在螢幕上的應用程式,你應該要覆寫哪一個生命週期的方法?

  • onDestroy()
  • onStop()
  • onPause()
  • onSaveInstanceState()

第 3 題

如要透過 Android 生命週期程式庫讓課程的生命週期感知,該類別應實作哪個介面?

  • Lifecycle
  • LifecycleOwner
  • Lifecycle.Event
  • LifecycleObserver

第 4 題

在下列哪一種情況下,您活動中的 onCreate() 方法會收到含有相關資料的Bundle (也就是Bundle不是 null)?可能會提供多個答案。

  • 活動會在裝置旋轉後重新啟動。
  • 活動是從零開始。
  • 從背景返回後,活動就會恢復。
  • 裝置已重新啟動。

開始下一個課程:5.1:ViewModel 和 ViewModelFactory

如要瞭解本課程中其他程式碼研究室的連結,請參閱 Android Kotlin 基礎程式碼程式碼到達網頁