Tina Tang's Blog

在哪裡跌倒了,就在哪裡躺下來

0%

Android筆記(23)-Activity生命週期的各個階段

身為 Android 開發人員,您必須瞭解 activity 生命週期(lifecycle) 。如果您的 activity 未正確回應生命週期狀態變更,應用程式可能會產生異常錯誤造成使用者混淆行為,或耗用過多 Android 系統資源。瞭解 Android 生命週期,並正確回應生命週期狀態變更,是成為 Android 優良使用者的重要條件。

學習目標

  • 如何將記錄資訊列印至 Logcat
  • Activity 生命週期的基本概念,以及 activity 於狀態之間變動時叫用的 callback。
  • 如何覆寫生命週期 callback 方法,以在 activity 生命週期的不同時間點執行運算

Activity 生命週期activity 在其生命週期內可呈現的狀態組合生命週期(lifecycle) 是指 activity 從最初建立到刪除的這段期間,且系統會回收該 activity 的資源。當使用者在應用程式 (包括應用程式內外) 的 activity 間進行瀏覽時,這些 activity 會在 activity 生命週期中的不同狀態之間進行轉換。


應用程式總覽

在本程式碼研究室中,您將使用名為 DessertClicker 的範例應用程式。在此應用程式中,每當使用者輕觸畫面上的甜點,應用程式就會為使用者「購買」甜點。這款應用程式會更新 layout 中的值,包括已購買的甜點數量,以及使用者花費的總金額

此應用程式含有數個與 Android 生命週期相關的錯誤 :舉例來說,應用程式會在特定情況下將甜點值重設為 0。瞭解 Android 生命週期有助於瞭解問題發生的原因與修正方法。

取得範例應用程式

下載 DessertClicker範例程式碼,並在 Android Studio 中開啟。

如果您使用 GitHub 中的範例程式碼,請注意資料夾名稱是 android-basics-kotlin-dessert-clicker-app-starter。在 Android Studio 中開啟專案時,請選取這個資料夾。


探索生命週期方法並新增基本記錄

每個 activity 都具有生命週期。這是植物和動物生命週期的影射,如同這隻蝴蝶的生命週期。蝴蝶的不同狀態,顯示其從出生到完全成熟的成蟲,再到死亡的成長過程。

同樣地,activity 生命週期 由 activity 可能經歷的不同狀態組成,從 activity 首次初始化到最終刪除,且系統回收其記憶體的這段時間。當使用者啟動您的應用程式、在 activity 間瀏覽、在應用程式內部或外部瀏覽時,activity 皆會改變狀態。下圖顯示所有 activity 生命週期狀態。顧名思義,這些狀態代表 activity 的狀態。

通常,當 activity 生命週期狀態變更時,您會想變更部分行為或執行某些程式碼。因此,Activity 類別本身和 Activity 的任何子類別 (例如 AppCompatActivity) 會實作一系列生命週期 回調(callback) 方法。

當 activity 狀態改變時,Android 會叫用這些 callback,而您可以在自己的 activity 中覆寫此類方法,並執行任務來回應生命週期狀態變更。下圖顯示生命週期狀態,以及可用的可覆寫 callback。

請務必瞭解這些 callback 的叫用時機,以及每個 callback 方法的處理方式。但兩個圖表都很複雜,可能會令人感到困惑。在本程式碼研究室中,您不僅將瞭解每個狀態和 callback 的意義,還會進行一些偵測工作並瞭解實際情況。

步驟 1:檢查 onCreate() 方法並新增記錄

如要瞭解 Android 生命週期發生的情況,瞭解呼叫各種生命週期方法的時機相當實用。這有助於您找出 DessertClicker 中的問題。

最簡單的方法就是使用 Android 紀錄(logging)功能紀錄(logging)功能可讓您在應用程式執行期間,將簡短訊息寫入主控台,以便顯示不同 callback 觸發的時間

  1. 執行 Dessert Clicker 應用程式,然後在甜點相片上輕觸數次。請注意已售出甜點值總金額的變化情況。

  2. 開啟 MainActivity.kt 並檢查此活動的 onCreate() 方法:

1
2
3
override fun onCreate(savedInstanceState: Bundle?) {
...
}

在活動生命週期圖表中,您可能已認出 onCreate() 方法,因為您先前使用過此 callback。這是每項 activity 都必須實作的方法。onCreate() 是您為 activity 執行任何一次性初始化作業的方法。舉例來說,在 onCreate() 中,您會加載 layout定義 click listeners,或是設定 view binding

當 activity 初始化後 (在記憶體中建立新的 Activity 物件時),系統會呼叫 onCreate() 生命週期方法一次。執行 onCreate() 後,系統會將 activity 視為已建立

注意事項: 覆寫 onCreate() 方法時,您必須呼叫父類別實作來完成 activity 建立,因此您必須立即在其內部呼叫 super.onCreate()。其他生命週期 callback 方法也是如此。

  1. onCreate() 方法中,於呼叫 super.onCreate() 後新增下列程式碼:
1
Log.d("MainActivity", "onCreate Called")
  1. 視需要匯入 Log 類別 (Mac 上請按 Alt+Enter 或 Option+Enter,然後選取「Import」)。如果您已啟用自動匯入功能,系統會自動執行這項操作。
1
import android.util.Log

Log 類別會將訊息寫入「Logcat」。「Logcat」是記錄訊息的主控台。此處會顯示來自 Android 的應用程式相關訊息,包括您透過 Log.d() 方法或其他 Log 類別方法明確傳送至記錄的訊息。

這個指令包含三個部分:

  • log message 的「優先順序(priority)」,也就是訊息的重要性。在這種情況下,Log.d() 方法會寫入偵錯(debug)訊息。Log 類別中的其他方法包括資訊(informational)訊息的 Log.i()、錯誤(errors)的 Log.e()、警示(warnings)的 Log.w(),或詳細(verbose)訊息的 Log.v()
  • log tag (第一個參數) ,在本範例中為 “MainActivity”。這個標記是 字串,供您在 Logcat 中輕鬆找到記錄訊息。標記通常是類別的名稱
  • log message (第二個參數)簡短字串,在本範例中為 “onCreate called”。

注意:在類別中宣告 TAG 常數是良好慣例:

1
const val TAG = "MainActivity"

並在記錄(logging)方法的後續呼叫中使用,如下所示:

1
Log.d(TAG, "onCreate Called")

編譯時間常數(compile-time constant)是不會改變的值。在變數宣告之前使用 const,以將其標示為編譯時間常數(compile-time constant)。

  1. 編譯並執行 DessertClicker 應用程式。輕觸甜點時,就不會在應用程式中看到任何行為差異。在 Android Studio 中,按一下畫面底部的「Logcat」分頁標籤。
  1. 在「Logcat」視窗的搜尋欄位中輸入 D/MainActivity

Logcat 可包含許多訊息,其中大部分無法派上用場。篩選 Logcat 項目的方法相當多樣,但搜尋是最簡單的方法。由於您在程式碼中使用 MainActivity 做為log tag,因此您可以使用該 tag 篩選記錄(log)。在開頭加上 D/,表示這是由 Log.d() 建立的偵錯(debug)訊息

D/已無法使用 –> 改成使用 level:debug 來篩選偵錯(debug)訊息、使用 tag: MainActivity 來篩選 MainActivity

您的 log message 包括日期和時間、套件名稱 (com.example.android.dessertclicker)、log tag,以及實際訊息。由於這則訊息會出現在記錄(log)中,但您知道系統已執行該 onCreate()

步驟 2:實作 onStart() 方法

系統將在 onCreate() 之後呼叫 onStart() 生命週期方法。onStart() 開始執行後,螢幕上會顯示您的 activity。onCreate() 僅可呼叫一次以初始化 activity,與之不同的是,onStart() 在 activity 生命週期中可呼叫多次

請注意,onStart() 已和對應的 onStop() 生命週期方法配對。如果使用者啟動應用程式,然後返回裝置的主畫面,activity 就會停止,且不再顯示於螢幕上

  1. 在 Android Studio,於 MainActivity.kt 開啟且游標位於 MainActivity 類別內時選取「Code」(程式碼) >「Override Methods」(覆寫方法),或按下 Control+o 。顯示的對話方塊會列出您可在此類別中覆寫的所有方法。
  1. 輸入 onStart 以搜尋正確方法。如要捲動至下一個相符的項目,請使用向下鍵。從清單中選擇 onStart(),然後按一下「OK」,插入樣板覆寫程式碼。程式碼如下所示:
1
2
3
override fun onStart() {
super.onStart()
}
  1. MainActivity.kt (位於類別宣告 class MainActivity. 上方) 頂層新增下列常數
1
const val TAG = "MainActivity"
  1. onStart() 方法中加入記錄(log)訊息:
1
2
3
4
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart Called")
}
  1. 編譯並執行 DessertClicker 應用程式,然後開啟「Logcat」窗格。在搜尋欄位中輸入 tag: MainActivity level:debug ,以篩選記錄。請注意,將逐一呼叫 onCreate()onStart() 方法,且您的 activity 會顯示在螢幕上。
  1. 按下裝置的主畫面按鈕,然後使用「最近使用」畫面返回 activity。請注意,activity 會從上次中斷的地方接續進行,所有值皆相同,且系統會將 onStart() 再次記錄到 Logcat。另請注意,通常不會再次呼叫 onCreate() 方法。

注意: 在測試裝置並觀察生命週期 callback 時,可能會在裝置旋轉時發現異常行為。稍後您將在本程式碼研究室中學習相關行為。

步驟 3:新增更多記錄陳述式

在這個步驟中,您會實作所有其他生命週期方法的記錄(logging)功能。

  1. 覆寫 MainActivity 中的其餘生命週期方法,並為每個方法新增陳述式。程式碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
override fun onResume() {
super.onResume()
Log.d(TAG, "onResume Called")
}

override fun onPause() {
super.onPause()
Log.d(TAG, "onPause Called")
}

override fun onStop() {
super.onStop()
Log.d(TAG, "onStop Called")
}

override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy Called")
}

override fun onRestart() {
super.onRestart()
Log.d(TAG, "onRestart Called")
}
  1. 再次編譯並執行 DessertClicker,並檢查 Logcat。這次請注意,除了 onCreate()onStart() 以外,系統也會提供 onResume() 生命週期 callback 的記錄(log)訊息。

activity 從頭開始時,系統會依序呼叫下列三個生命週期 callback:

  • onCreate() 可建立應用程式。
  • onStart() 可啟動 activity 並在螢幕上顯示。
  • onResume()聚焦(focus)在 activity,並使其準備與使用者進行互動

儘管有名稱,即使沒有可繼續進行的操作,啟動時仍會呼叫 onResume() 方法。


探索生命週期用途

現在,DessertClicker 應用程式已進行記錄設定,您可以透過各種方式開始使用該應用程式,並探索如何觸發生命週期回呼來回應用途。

用途 1:開啟及關閉 activity

您將從最基本的用途開始,也就是初次啟動應用程式,然後完全關閉應用程式。

  1. 編譯並執行 DessertClicker 應用程式 (如果尚未執行)。如您所見,活動初次開始時,就會呼叫 onCreate()onStart(),以及 onResume() 回呼。
  1. 輕觸杯子蛋糕數次。

  2. 關閉應用程式。請注意,Logcat 中會依序呼叫 onPause()onStop()onDestroy()

  1. 重新開啟 DessertClicker 應用程式。Logcat 如下:

注意: 此處重點為 onCreate()onDestroy() 在單一活動執行個體的生命週期中只呼叫一次:onCreate() 會首次初始化應用程式,onDestroy() 會清除應用程式使用的資源。

用途 2:離開後再返回 activity

現在,您已啟動並完全關閉應用程式,您可看到 activity 首次建立時的大部分生命週期狀態。此外,您也會看到 activity 完全關閉且刪除後經歷的所有生命週期狀態。然而,當使用者與 Android 裝置互動時,會在應用程式間切換、返回主畫面、啟動新的應用程式,並處理因其他 activity (例如來電) 而中斷的 activity。

每次使用者離開該 activity 時,activity 都不會完全關閉

  • 當系統不再於畫面中顯示 activity 時,即稱為使 activity 進入背景。(反之,則 activity 位於前景或螢幕上。)
  • 使用者返回您的應用程式時,系統會重新啟動相同的 activity,並再次顯示該 activity。生命週期中的此部分稱為應用程式的可見生命週期。

應用程式在背景運作時,通常不應主動運作,以維持系統資源和電池壽命。您會使用 Activity 生命週期及其 callback 來瞭解應用程式移至背景的時間,以便暫停任何進行中的作業。接著您會在應用程式進入前景時重新開始作業。

在這個步驟中,您可以查看應用程式進入背景後再次回到前景的 activity 生命週期

  1. 開啟 DessertClicker 應用程式,並點選杯子蛋糕數次。
  1. 按下裝置上的「主畫面」按鈕,然後觀察 Android Studio 中的 Logcat。返回主畫面會讓應用程式進入背景,而非完全關閉應用程式。請注意,系統會呼叫 onPause() 方法和 onStop() 方法,但不會呼叫 onDestroy() 方法。

呼叫 onPause() 後,應用程式就不再有焦點onStop() 後,應用程式就不會再顯示於螢幕上。儘管 activity 已經停止,但 Activity 物件仍位於背景的記憶體中。系統並未刪除該 activity。使用者可能會返回應用程式,因此 Android 會保留您的 activity 資源。

  1. 使用最近使用畫面返回應用程式。請注意,activity 會在 Logcat 中使用 onRestart()onStart() 重新啟動,然後使用 onResume() 重新啟用。

當 activity 返回前景時,就不會再次呼叫 onCreate() 方法。activity 物件並未刪除,因此不需要重新建立。系統會呼叫 onRestart() 方法,而不是 onCreate()。請注意,當 activity 返回前景時,系統會保留「Desserts Sold」數量。

  1. 至少啟動一個 DessertClicker 以外的應用程式,使裝置的最近使用畫面中含有多個應用程式。

  2. 開啟最近使用畫面,並開啟其他最近 activity。接著返回最近使用的應用程式,並將 DessertClicker 移回前景。

  1. 來回切換應用程式

請注意,此處 Logcat 中顯示的回呼與按下主畫面按鈕時相同。應用程式進入背景時,系統會呼叫 onPause()onStop(),並在應用程式返回時呼叫 onRestart()onStart()onResume()

注意: 此處重點是,當使用者往返瀏覽 activity 時,系統會多次呼叫 onStart()onStop()

當應用程式停止並移至背景,或應用程式返回前景並再次啟動時,系統就會呼叫這些方法。如果遇到這類情況,而您必須在應用程式中執行工作,請覆寫相關的生命週期 callback 方法。

那麼,onRestart() 呢?onRestart() 方法與 onCreate() 類似。在 activity 顯示之前,系統會呼叫 onCreate()onRestart()。第一次僅會呼叫 onCreate() 方法,onRestart() 則在後續呼叫。onRestart() 方法是放置程式碼的地方,只有在活動為「非」初次啟動時才會呼叫該程式碼

用途 3:部分隱藏 activity

您已經瞭解應用程式啟動且呼叫 onStart() 時,螢幕上會顯示該應用程式。重新啟用應用程式並呼叫 onResume() 時,應用程式會取得使用者焦點,即使用者可與應用程式互動。應用程式完全顯示在畫面上,且有使用者焦點時,這個生命週期部分便是「互動」生命週期。

應用程式進入背景時,焦點會於 onPause() 後消失,且應用程式在 onStop() 後也不再顯示。

焦點和可見度之間的差異非常重要,因為 activity 在畫面上可以部分顯示,但沒有使用者焦點。在此步驟中,可查看部分顯示,但沒有使用者焦點的 activity。

  1. 在 DessertClicker 應用程式執行時,按一下畫面右上方的「Share」按鈕。
    分享 activity 會顯示在畫面的下半部,但 activity 仍會在上半部顯示

  1. 檢查 Logcat,且注意僅呼叫 onPause()

在此用途中,系統不會呼叫 onStop(),因為 activity 仍部分顯示。不過,這類 activity 沒有使用者焦點,且使用者無法與其互動,因為位於前景的「分享」activity 具有使用者焦點。

為什麼這個差異很重要?僅使用 onPause() 的干擾通常只會持續一小段時間,然後使用者便會返回 activity 或前往其他 activity 或應用程式。一般而言,您需要持續更新 UI,應用程式的其餘部分便不會停止運作。

onPause() 中執行的程式碼會使其他內容無法顯示,因此請將程式碼保存在輕量 onPause() 中。例如,如果有來電,onPause() 中的程式碼可能會延遲來電通知。

  1. 按一下分享對話方塊以外的地方,返回應用程式,並留意系統已呼叫 onResume()

onResume()onPause() 都必須與焦點有關。活動有焦點時,系統會呼叫 onResume() 方法,而活動失去聚焦時,就會呼叫 onPause()


探索設定變更

管理 activity 生命週期的另一個用例也十分重要:設定變更對 activity 生命週期的影響。

設定變更於裝置狀態改變時發生,因此基本上,使系統解決變更的最簡單方法就是完全關閉,然後重新建立 activity。舉例來說,若使用者變更裝置語言,您必須調整整個版面配置,以配合不同的文字方向和字串長度。如果使用者將裝置插入座架或新增實體鍵盤,應用程式版面配置可能必須採用不同的顯示大小或版面配置。如果裝置螢幕方向改變 (例如將裝置從直向轉為橫向或向後旋轉),您可能需要根據新的螢幕方向變更版面配置。讓我們看看應用程式在這個情境中的行為。

裝置旋轉時資料遺失

  1. 編譯並執行應用程式,然後開啟 Logcat。

  2. 將裝置或模擬器旋轉至橫向模式。您可以使用旋轉按鈕或 Control 方向鍵,將模擬器向左或向右旋轉。

  1. 檢查 Logcat 中的輸出內容。在 MainActivity 上篩選輸出內容。

請注意,當裝置或模擬器旋轉螢幕時,系統會呼叫所有生命週期 callback 來關閉 activity。接著,當您重新建立 activity 時,系統會呼叫所有生命週期 callback 來啟動 activity

裝置旋轉且關閉並重新建立 activity 後,activity 就會以預設值啟動,也就是將甜點售出數量和收益重設為零。

使用 onSaveInstanceState() 儲存套件資料

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

您可以將 onSaveInstanceState() 呼叫視為安全防護措施;此呼叫會在 activity 離開前景時,將少量資訊儲存至套件。系統會在此時儲存資料,因為若等待直到應用程式關閉,系統可能會有資源壓力。

注意: Android 有時會關閉整個應用程式處理程序,包括與該應用程式相關的所有活動。Android 會在系統處於壓力且發生視覺延遲問題時執行此類關閉程序,因此目前不會執行其他 callback 或程式碼。應用程式處理程序只是在背景中自動關閉。但對使用者而言,應用程式似乎沒有關閉。當使用者返回 Android 系統已關閉的應用程式時,Android 就會重新啟動該應用程式。您必須確保使用者不會在此情況下發生任何資料遺失情形

每次儲存資料可確保套件中的已更新資料能夠視需要還原。

  1. 在 MainActivity 中覆寫 onSaveInstanceState() callback,並新增 log 陳述式。
1
2
3
4
5
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)

Log.d(TAG, "onSaveInstanceState Called")
}

注意: onSaveInstanceState() 有兩種覆寫設定,一種只有 outState 參數,另一種則包含 outStateoutPersistentState 參數。請使用上述程式碼中顯示的單一 outState 參數。

  1. 編譯並執行應用程式,然後按一下「Home」按鈕,讓應用程式進入背景。請注意,onSaveInstanceState() callback 緊接在 onPause()onStop() 之後:
  1. 在檔案頂端的類別定義前方,加入以下常數:
1
2
const val KEY_REVENUE = "revenue_key"
const val KEY_DESSERT_SOLD = "dessert_sold_key"
  • 您將使用這些鍵儲存與擷取執行個體狀態套件(instance state bundle)中的資料。
  1. 向下捲動至 onSaveInstanceState(),並注意 outState 參數為 Bundle 類型。Bundle 是一系列 key-value 組合,其中 key 一律為字串(String)。您可以將簡易的資料 (例如 IntBoolean 值) 加入套件中。由於系統會將此套件儲存在記憶體中,因此最佳做法是讓套件中的資料保持精簡。此套件的大小有限,但尺寸會因裝置而異。如果儲存過多資料,應用程式可能會因為 TransactionTooLargeException error 而異常終止。

  2. onSaveInstanceState() 中,使用 putInt() 方法將 revenue 值 (Int) 加入套件中:

1
outState.putInt(KEY_REVENUE, revenue)

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

  1. 按照相同的甜點售出數量重複上述步驟:
1
outState.putInt(KEY_DESSERT_SOLD, dessertsSold)

使用 onCreate() 還原套件資料

您可以在 onCreate(Bundle)onRestoreInstanceState(Bundle) 中還原 activity 狀態 (由 onSaveInstanceState() 方法填入的 Bundle 會傳遞至這兩個生命週期 callback 方法)。

  1. 向上捲動至 onCreate(),並檢查方法簽章(signature):
1
override fun onCreate(savedInstanceState: Bundle?) {

請注意,每次呼叫時,onCreate() 都會獲得一個 Bundle。當您的 activity 因程序關閉而重新啟動時,您儲存的套件會傳遞至 onCreate()。如果您的 activity 重新啟動,onCreate() 中的此 Bundlenull。因此,如果套件並非 null,表示您是從先前的已知點「重新建立」activity。

注意: 如果正在重新建立 activity,系統會在 onStart() 後呼叫含有套件的 onRestoreInstanceState() callback。在大多數情況下,您可以還原 onCreate() 的 activity 狀態。不過,由於在 onStart() 之後呼叫 onRestoreInstanceState(),如果您需要在呼叫 onCreate() 後還原部分狀態,可以使用 onRestoreInstanceState()

  1. 在設定 binding 變數後,將此程式碼新增至 onCreate()
1
2
3
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}

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

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

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

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

  1. 新增 getInt() 方法,還原收益和甜點售出數量。
1
2
3
4
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
}
  1. 編譯並執行應用程式。按下杯子蛋糕至少五次,直到切換成甜甜圈為止。
  1. 旋轉裝置。請注意,應用程式現在可以顯示正確的收益,以及套件中的甜點售出值。另請注意,甜點已恢復為杯子蛋糕。
  1. 在 MainActivity 中檢查 showCurrentDessert() 方法。請注意,此方法會根據目前的甜點售出數量和 allDesserts 變數中的甜點清單,判斷要在 activity 中顯示的甜點圖片。
1
2
3
4
5
6
for (dessert in allDesserts) {
if (dessertsSold >= dessert.startProductionAmount) {
newDessert = dessert
}
else break
}

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

  1. onCreate() 中,從套件還原狀態的區塊呼叫 showCurrentDessert()
1
2
3
4
5
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
showCurrentDessert()
}
  1. 編譯並執行應用程式,然後旋轉畫面。請注意,甜點售出值、總收益和甜點圖片已順利還原。

總結

activity 生命週期

  • activity 生命週期是一組 activity 轉移的狀態。activity 生命週期是從首次建立時開始,並在 activity 刪除時結束。
  • 使用者在不同 activity 之間及應用程式內外瀏覽時,每個 activity 會在 activity 生命週期中的狀態間切換。
  • activity 生命週期中的每個狀態都有相應的 callback 方法,可在 Activity 類別中覆寫,如下:onCreate() onStart() onPause() onRestart() onResume() onStop() onDestroy()
  • 如要新增在 activity 轉換為生命週期狀態時發生的行為,請覆寫狀態的 callback 方法
  • 如要在 Android Studio 中為類別新增架構覆寫方法,請依序選取「Code」>「Override Methods」,或按下 Control+o

使用 Log 進行記錄

  • Android Logging API (尤其是 Log 類別),可讓您編寫在 Android Studio 的 Logcat 中顯示的簡短訊息。
  • 請使用 Log.d() 編寫偵錯訊息。這個方法使用兩個引數:log tag,通常是類別名稱,以及 log message,此為簡短字串。
  • 使用 Android Studio 中的「Logcat」視窗檢視系統記錄,包括您編寫的訊息。

保留 activity 狀態

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

設定變更

  • 設定變更於裝置狀態改變時發生,因此基本上,使系統解決變更的最簡單方法就是刪除,然後重新建立 activity
  • 最常見的設定變更範例為,使用者將裝置從直向轉為橫向模式,或從橫向轉為直向模式時。裝置語言變更或外接硬體鍵盤時,也可能會發生設定變更。
  • 發生設定變更時,Android 會叫用所有 activity 生命週期的關閉 callback。接著,Android 會從頭重新啟動 activity,並執行所有生命週期啟動 callback。
  • Android 因設定變更而關閉應用程式時,Android 會以 onCreate() 可用的狀態套件來重新啟動 activity。
  • 如同程序中斷,請將應用程式的狀態儲存到 onSaveInstanceState() 的套件中。