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. 在「Project」檢視畫面中,依序展開「java」>「com.example.android.dessertclicker」,然後開啟 DessertTimer.kt。請注意,目前所有程式碼都已註解,因此不會做為應用程式的一部分執行。
  3. 選取編輯器視窗中的所有程式碼。依序選取「Code」>「Comment with Line Comment」,或按 Control+/ (Mac 上為 Command+/)。這個指令會取消註解檔案中的所有程式碼。(在您重建應用程式之前,Android Studio 可能會顯示未解決的參照錯誤)。
  4. 請注意,DessertTimer 類別包含 startTimer()stopTimer(),可啟動及停止計時器。startTimer() 執行時,計時器每秒都會列印記錄訊息,並顯示計時器已執行的總秒數。stopTimer() 方法會停止計時器和記錄陳述式。
  1. 開啟 MainActivity.kt。在類別頂端,緊接在 dessertsSold 變數下方,新增計時器變數:
private lateinit var dessertTimer : DessertTimer;
  1. 向下捲動至 onCreate(),並在呼叫 setOnClickListener() 後建立新的 DessertTimer 物件:
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. 使用最近使用畫面返回應用程式。請注意,計時器會在 Logcat 中從 0 重新啟動。
  4. 按一下「分享」按鈕。請注意,Logcat 中的計時器仍在運作。

  5. 按一下「首頁」按鈕。請注意,Logcat 中的計時器會停止運作。
  6. 使用最近使用畫面返回應用程式。請注意,Logcat 中的計時器會從上次停止的地方繼續計時。
  7. MainActivityonStop() 方法中,註解掉對 stopTimer() 的呼叫。註解掉 stopTimer() 可示範在 onStart() 中啟動作業,但忘記在 onStop() 中再次停止作業的情況。
  8. 編譯並執行應用程式,然後在計時器啟動後按一下「Home」按鈕。即使應用程式在背景執行,計時器仍會持續使用系統資源。讓計時器繼續執行會導致應用程式發生記憶體洩漏,這可能不是您想要的行為。

    一般模式是在回呼中設定或啟動某個項目時,在對應的回呼中停止或移除該項目。這樣一來,您就不必在不再需要時讓任何項目繼續執行。
  1. onStop() 中,取消註解停止計時器的程式碼行。
  2. onStart() 剪下並貼上 startTimer() 呼叫至 onCreate()。這項變更示範了在 onCreate() 中初始化及啟動資源的案例,而不是使用 onCreate() 初始化資源,並使用 onStart() 啟動資源。
  3. 編譯並執行應用程式。請注意,計時器會如預期開始運作。
  4. 按一下「Home」停止應用程式。計時器會如預期停止運作。
  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. 使用 ON_STOP 事件對 stopTimer() 執行相同操作:
@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 應用程式搭乘公車,請務必快速顯示該應用程式,並持續顯示路線。使用者可能幾天沒看 DessertClicker 應用程式,因此讓該應用程式在背景順暢執行就沒那麼重要。

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

有時 Android 甚至會關閉整個應用程式程序,包括與該應用程式相關的所有活動。當系統有壓力且可能發生視覺延遲時,Android 就可能執行此類關閉程序,因此目前不會執行其他回呼或程式碼。應用程式處理程序只是在背景中自動關閉。但對使用者而言,應用程式似乎沒有關閉。當使用者返回 Android OS 關閉的應用程式時,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 指令是否位於執行路徑中。如需操作說明,請參閱 公用程式章節中的「將 adb 新增至執行路徑」。
  5. 複製這則註解並貼到指令列,然後按下 Return 鍵:
adb shell am kill com.example.android.dessertclicker

這項指令會告知所有連線的裝置或模擬器停止執行套件名稱為 dessertclicker 的程序,但前提是應用程式必須在背景執行。由於應用程式在背景執行,裝置或模擬器螢幕上不會顯示任何資訊,指出程序已停止。在 Android Studio 中,按一下「Run」分頁標籤,查看「Application terminated」(應用程式已終止) 的訊息。按一下「Logcat」分頁標籤,即可看到系統從未執行 onDestroy() 回呼,活動只是結束而已。

  1. 使用「最近使用」畫面返回應用程式。無論應用程式是進入背景還是完全停止,都會顯示在「最近使用」畫面中。使用最近使用畫面返回應用程式時,活動會再次啟動。活動會經歷整組啟動生命週期回呼,包括 onCreate()
  2. 請注意,應用程式重新啟動時,會將「分數」(售出甜點數量和總金額) 重設為預設值 (0)。如果 Android 關閉了應用程式,為什麼沒有儲存狀態?

    當作業系統為您重新啟動應用程式時,Android 會盡量將應用程式重設為先前的狀態。每當您離開活動時,Android 會擷取部分檢視區塊的狀態,並儲存在套件中。系統會自動儲存的資料包括 EditText 中的文字 (只要這些文字在版面配置中設有 ID),以及活動的返回堆疊。

    不過,有時 Android OS 並不瞭解您的所有資料。舉例來說,如果您在 DessertClicker 應用程式中加入 revenue 等自訂變數,Android 作業系統不會知道這項資料,也不會瞭解這項資料對您活動的重要性。您必須自行將這項資料新增至套件。

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

onSaveInstanceState() 方法為回呼,可用於在 Android 作業系統刪除應用程式時儲存您需要的任何資料。在生命週期回呼圖中,系統會在活動停止後呼叫 onSaveInstanceState()。每當應用程式進入背景時就會呼叫此方法。

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

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

   Timber.i("onSaveInstanceState Called")
}
  1. 編譯並執行應用程式,然後按一下「Home」按鈕,讓應用程式進入背景。請注意,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(),並注意 outState 參數為 Bundle 類型。

    套件是一系列鍵/值組合,其中鍵一律為字串。您可以將原始值 (例如 intboolean 值) 加入套件中。
    由於系統會將此套件儲存在 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. 在設定 DessertTimer 後,將此程式碼新增至 onCreate()
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 因設定變更而關閉應用程式時,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 基礎知識程式碼研究室登陸頁面