1. 歡迎
說明
在本程式碼研究室中,您將進一步瞭解 Android 的基本要素:「活動」。活動生命週期是活動在其生命週期內可呈現的狀態組合。生命週期是指活動從最初建立到刪除的這段期間,且系統會回收該活動的資源。當使用者在應用程式 (包括應用程式內外) 的活動間進行瀏覽時,這些活動會在活動生命週期中的不同狀態之間進行轉換。
身為 Android 開發人員,您必須瞭解活動生命週期。如果您的活動未正確回應生命週期狀態變更,應用程式可能會產生異常錯誤、造成使用者混淆行為,或耗用過多 Android 系統資源。瞭解 Android 生命週期,並正確回應生命週期狀態變更,是成為 Android 優良使用者的重要條件。
須知事項
- 什麼是活動,以及在應用程式中建立活動的方式。
- 活動
onCreate()
方法的運作內容,以及該方法執行的運算種類。
課程內容
- 如何將記錄資訊列印至 Logcat。
Activity
生命週期的基本概念,以及活動於狀態之間變動時叫用的回呼。- 如何覆寫生命週期回呼方法,以在活動生命週期的不同時間點執行運算。
執行步驟
- 修改名稱為 DessertClicker 的範例應用程式,以便新增 Logcat 中顯示的記錄資訊。
- 覆寫生命週期回呼方法,並記錄活動狀態的變更。
- 執行應用程式,並記下活動開始、停止和繼續時的記錄資訊。
- 實作
onSaveInstanceState()
方法,保留因裝置設定變更而可能遺失的應用程式資料。新增程式碼,以在應用程式再次啟動時還原該資料。
2. 應用程式總覽
在本程式碼研究室中,您將使用名為 DessertClicker 的範例應用程式。在此應用程式中,每當使用者輕觸畫面上的甜點,應用程式就會為使用者「購買」甜點。這款應用程式會更新版面配置中的值,包括已購買的甜點數量,以及使用者花費的總金額。
此應用程式含有數個與 Android 生命週期相關的錯誤:舉例來說,應用程式會在特定情況下將甜點值重設為 0。瞭解 Android 生命週期有助於瞭解問題發生的原因與修正方法。
取得範例應用程式
下載 DessertClicker 範例程式碼,並在 Android Studio 中開啟。
如果您使用 GitHub 中的範例程式碼,請注意資料夾名稱是 android-basics-kotlin-dessert-clicker-app-starter
。在 Android Studio 中開啟專案時,請選取這個資料夾。
如要取得這個程式碼研究室的程式碼,並在 Android Studio 中開啟,請按照下列步驟操作:
取得程式碼
- 按一下上面顯示的網址。系統會在瀏覽器中開啟專案的 GitHub 頁面。
- 在專案的 GitHub 頁面中,按一下「Code」按鈕開啟對話方塊。
- 在對話方塊中,按一下「Download ZIP」按鈕,將專案儲存到電腦。等待下載作業完成。
- 在電腦中找到該檔案 (可能位於「下載」資料夾中)。
- 按兩下解壓縮 ZIP 檔案。這項操作會建立含有專案檔案的新資料夾。
在 Android Studio 中開啟專案
- 啟動 Android Studio。
- 在「Welcome to Android Studio」視窗中,按一下「Open an existing Android Studio project」。
注意:如果 Android Studio 已開啟,請依序選取「File」>「New」>「Import Project」選單選項。
- 在「Import Project」對話方塊中,前往解壓縮專案資料夾所在的位置 (可能位於「下載」資料夾中)。
- 按兩下該專案資料夾。
- 等待 Android Studio 開啟專案。
- 按一下「Run」按鈕 即可建構並執行應用程式。請確認應用程式的建構符合預期。
- 在「Project」工具視窗中瀏覽專案檔案,查看應用程式的設定方式。
3. 探索生命週期方法並新增基本記錄
每個活動都具有生命週期。這是植物和動物生命週期的影射,如同這隻蝴蝶的生命週期。蝴蝶的不同狀態,顯示其從出生到完全成熟的成蟲,再到死亡的成長過程。
同樣地,活動生命週期由活動可能經歷的不同狀態組成,從活動首次初始化到最終刪除,且系統回收其記憶體的這段時間。當使用者啟動您的應用程式、在活動間瀏覽、在應用程式內部或外部瀏覽時,活動皆會改變狀態。下圖顯示所有活動生命週期狀態。顧名思義,這些狀態代表活動的狀態。
通常,當活動生命週期狀態變更時,您會想變更部分行為或執行某些程式碼。因此,Activity
類別本身和 Activity
的任何子類別 (例如 AppCompatActivity
) 會實作一系列生命週期回呼方法。當活動狀態改變時,Android 會叫用這些回呼,而您可以在自己的活動中覆寫此類方法,並執行任務來回應生命週期狀態變更。下圖顯示生命週期狀態,以及可用的可覆寫回呼。
請務必瞭解這些回呼的叫用時機,以及每個回呼方法的處理方式。但兩個圖表都很複雜,可能會令人感到困惑。在本程式碼研究室中,您不僅將瞭解每個狀態和回呼的意義,還會進行一些偵測工作並瞭解實際情況。
步驟 1:檢查 onCreate() 方法並新增記錄
如要瞭解 Android 生命週期發生的情況,瞭解呼叫各種生命週期方法的時機相當實用。這有助於您找出 DessertClicker 中的問題。
最簡單的方法就是使用 Android 記錄功能。記錄功能可讓您在應用程式執行期間,將簡短訊息寫入主控台,以便顯示不同回呼觸發的時間。
- 執行 Dessert Clicker 應用程式,然後在甜點相片上輕觸數次。請注意,已售出甜點值和總金額的變化情況。
- 開啟
MainActivity.kt
並檢查此活動的onCreate()
方法:
override fun onCreate(savedInstanceState: Bundle?) {
...
}
在活動生命週期圖表中,您可能已認出 onCreate()
方法,因為您先前使用過此回呼。這是每項活動都必須實作的方法。onCreate()
是您為活動執行任何一次性初始化作業的方法。舉例來說,在 onCreate()
中,您會加載版面配置、定義點選監聽器,或是設定檢視繫結。
當活動初始化後 (在記憶體中建立新的 Activity
物件時),系統會呼叫 onCreate()
生命週期方法一次。執行 onCreate()
後,系統會將活動視為已建立。
- 在
onCreate()
方法中,於呼叫super.onCreate()
後新增下列程式碼:
Log.d("MainActivity", "onCreate Called")
- 視需要匯入
Log
類別 (Mac 上請按Alt+Enter
或Option+Enter
,然後選取「Import」)。如果您已啟用自動匯入功能,系統會自動執行這項操作。
import android.util.Log
Log
類別會將訊息寫入「Logcat」。「Logcat」是記錄訊息的主控台。此處會顯示來自 Android 的應用程式相關訊息,包括您透過 Log.d()
方法或其他 Log
類別方法明確傳送至記錄的訊息。
這個指令包含三個部分:
- 記錄訊息的「優先順序」,也就是訊息的重要性。在這種情況下,
Log.d()
方法會寫入偵錯訊息。Log
類別中的其他方法包括資訊訊息的Log.i()
、錯誤的Log.e()
、警示的Log.w()
,或詳細訊息的Log.v()
。 - 記錄「標記」 (第一個參數),在本範例中為
"MainActivity"
。這個標記是字串,供您在 Logcat 中輕鬆找到記錄訊息。標記通常是類別的名稱。 - 實際記錄訊息 (第二個參數) 是簡短字串,在本範例中為
"onCreate called"
。
編譯時間常數是不會改變的值。在變數宣告之前使用 const
,以將其標示為編譯時間常數。
- 編譯並執行 DessertClicker 應用程式。輕觸甜點時,就不會在應用程式中看到任何行為差異。在 Android Studio 中,按一下畫面底部的「Logcat」分頁標籤。
- 在「Logcat」視窗的搜尋欄位中輸入
D/MainActivity
。
Logcat 可包含許多訊息,其中大部分無法派上用場。篩選 Logcat 項目的方法相當多樣,但搜尋是最簡單的方法。由於您在程式碼中使用 MainActivity
做為記錄標記,因此您可以使用該標記篩選記錄。在開頭加上 D/
,表示這是由 Log.d()
建立的偵錯訊息。
您的記錄訊息包括日期和時間、套件名稱 (com.example.android.dessertclicker
)、記錄標記 (開頭為 D/
),以及實際訊息。由於這則訊息會出現在記錄中,但您知道系統已執行該 onCreate()
。
步驟 2:實作 onStart() 方法
系統將在 onCreate()
之後呼叫 onStart()
生命週期方法。onStart()
開始執行後,螢幕上會顯示您的活動。onCreate()
僅可呼叫一次以初始化活動,與之不同的是,onStart()
在活動生命週期中可呼叫多次。
請注意,onStart()
已和對應的 onStop()
生命週期方法配對。如果使用者啟動應用程式,然後返回裝置的主畫面,活動就會停止,且不再顯示於螢幕上。
- 在 Android Studio,於
MainActivity.kt
開啟且游標位於MainActivity
類別內時選取「Code」(程式碼) >「Override Methods」(覆寫方法),或按下Control+o
(Mac 上的Command+o
)。顯示的對話方塊會列出您可在此類別中覆寫的所有方法。
- 輸入
onStart
以搜尋正確方法。如要捲動至下一個相符的項目,請使用向下鍵。從清單中選擇onStart()
,然後按一下「OK」,插入樣板覆寫程式碼。程式碼如下所示:
override fun onStart() {
super.onStart()
}
- 在
MainActivity.kt
(位於類別宣告class
MainActivity.
上方) 頂層新增下列常數
const val TAG = "MainActivity"
- 在
onStart()
方法中加入記錄訊息:
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart Called")
}
- 編譯並執行 DessertClicker 應用程式,然後開啟「Logcat」窗格。在搜尋欄位中輸入
D/MainActivity
,以篩選記錄。請注意,將逐一呼叫onCreate()
和onStart()
方法,且您的活動會顯示在螢幕上。 - 按下裝置的主畫面按鈕,然後使用「最近使用」畫面返回活動。請注意,活動會從上次中斷的地方接續進行,所有值皆相同,且系統會將
onStart()
再次記錄到 Logcat。另請注意,通常不會再次呼叫onCreate()
方法。
16:19:59.125 31107-31107/com.example.android.dessertclicker D/MainActivity: onCreate Called 16:19:59.372 31107-31107/com.example.android.dessertclicker D/MainActivity: onStart Called 16:20:11.319 31107-31107/com.example.android.dessertclicker D/MainActivity: onStart Called
步驟 3:新增更多記錄陳述式
在這個步驟中,您會實作所有其他生命週期方法的記錄功能。
- 覆寫
MainActivity
中的其餘生命週期方法,並為每個方法新增陳述式。程式碼如下:
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")
}
- 再次編譯並執行 DessertClicker,並檢查 Logcat。這次請注意,除了
onCreate()
和onStart()
以外,系統也會提供onResume()
生命週期回呼的記錄訊息。
2020-10-16 10:27:33.244 22064-22064/com.example.android.dessertclicker D/MainActivity: onCreate Called 2020-10-16 10:27:33.453 22064-22064/com.example.android.dessertclicker D/MainActivity: onStart Called 2020-10-16 10:27:33.454 22064-22064/com.example.android.dessertclicker D/MainActivity: onResume Called
活動從頭開始時,系統會依序呼叫下列三個生命週期回呼:
onCreate()
可建立應用程式。onStart()
可啟動活動並在螢幕上顯示。onResume()
可聚焦於活動,並使其準備與使用者進行互動。
儘管有名稱,即使沒有可繼續進行的操作,啟動時仍會呼叫 onResume()
方法。
4. 探索生命週期用途
現在,DessertClicker 應用程式已進行記錄設定,您可以透過各種方式開始使用該應用程式,並探索如何觸發生命週期回呼來回應用途。
用途 1:開啟及關閉活動
您將從最基本的用途開始,也就是初次啟動應用程式,然後完全關閉應用程式。
- 編譯並執行 DessertClicker 應用程式 (如果尚未執行)。如您所見,活動初次開始時,就會呼叫
onCreate()
、onStart()
,以及onResume()
回呼。
2020-10-16 10:27:33.244 22064-22064/com.example.android.dessertclicker D/MainActivity: onCreate Called 2020-10-16 10:27:33.453 22064-22064/com.example.android.dessertclicker D/MainActivity: onStart Called 2020-10-16 10:27:33.454 22064-22064/com.example.android.dessertclicker D/MainActivity: onResume Called
- 輕觸杯子蛋糕數次。
- 輕觸裝置上的「Back」按鈕。請注意,Logcat 中會依序呼叫
onPause()
、onStop()
和onDestroy()
。
2020-10-16 10:31:53.850 22064-22064/com.example.android.dessertclicker D/MainActivity: onPause Called 2020-10-16 10:31:54.620 22064-22064/com.example.android.dessertclicker D/MainActivity: onStop Called 2020-10-16 10:31:54.622 22064-22064/com.example.android.dessertclicker D/MainActivity: onDestroy Called
在此情況下,使用「Back」按鈕會使活動 (和應用程式) 完全關閉。執行 onDestroy()
方法表示活動已完全終止,且可做為垃圾收集。垃圾收集是指自動清理不再使用的物件。呼叫 onDestroy()
後,系統會知道這些資源皆可捨棄,且會開始清除該記憶體。
若程式碼手動呼叫活動的 finish()
方法,或是使用者強制退出應用程式,系統也會將活動完全關閉。(例如,使用者可在最近使用畫面中強制退出或關閉應用程式)。若應用程式長時間未處於畫面上,Android 系統也可能會自行關閉您的活動。Android 會藉此節省電池電力,並允許其他應用程式使用您應用程式的資源。
- 在總覽畫面上找到所有開啟的應用程式,返回 DessertClicker 應用程式 (請注意,這也稱為最近使用畫面或最近使用的應用程式)。Logcat 如下:
2020-10-16 10:31:54.622 22064-22064/com.example.android.dessertclicker D/MainActivity: onDestroy Called 2020-10-16 10:38:00.733 22064-22064/com.example.android.dessertclicker D/MainActivity: onCreate Called 2020-10-16 10:38:00.787 22064-22064/com.example.android.dessertclicker D/MainActivity: onStart Called 2020-10-16 10:38:00.788 22064-22064/com.example.android.dessertclicker D/MainActivity: onResume Called
活動已在前一個步驟中遭到刪除,因此當您返回應用程式時,Android 會開始新的活動,並呼叫 onCreate()
、onStart()
和 onResume()
方法。請注意,系統不會保留先前活動的任何 DessertClicker 記錄。
onCreate()
方法是重要步驟;在這裡可以執行所有初次初始化作業、透過加載首次設定版面配置,以及初始化變數。
用途 2:離開後再返回活動
現在,您已啟動並完全關閉應用程式,您可看到活動首次建立時的大部分生命週期狀態。此外,您也會看到活動完全關閉且刪除後經歷的所有生命週期狀態。然而,當使用者與 Android 裝置互動時,會在應用程式間切換、返回主畫面、啟動新的應用程式,並處理因其他活動 (例如來電) 而中斷的活動。
每次使用者離開該活動時,活動都不會完全關閉:
- 當系統不再於畫面中顯示活動時,即稱為使活動進入背景。(反之,則活動位於前景或螢幕上。)
- 使用者返回您的應用程式時,系統會重新啟動相同的活動,並再次顯示該活動。生命週期中的此部分稱為應用程式的可見生命週期。
應用程式在背景運作時,通常不應主動運作,以維持系統資源和電池壽命。您會使用 Activity
生命週期及其回呼來瞭解應用程式移至背景的時間,以便暫停任何進行中的作業。接著您會在應用程式進入前景時重新開始作業。
在這個步驟中,您可以查看應用程式進入背景後再次回到前景的活動生命週期。
- 執行 DessertClicker 應用程式時,點選杯子蛋糕數次。
- 按下裝置上的「主畫面」按鈕,然後觀察 Android Studio 中的 Logcat。返回主畫面會讓應用程式進入背景,而非完全關閉應用程式。請注意,系統會呼叫
onPause()
方法和onStop()
方法,但不會呼叫onDestroy()
方法。
2020-10-16 10:41:05.383 22064-22064/com.example.android.dessertclicker D/MainActivity: onPause Called 2020-10-16 10:41:05.966 22064-22064/com.example.android.dessertclicker D/MainActivity: onStop Called
呼叫 onPause()
後,應用程式就不再有焦點。onStop()
後,應用程式就不會再顯示於螢幕上。儘管活動已經停止,但 Activity
物件仍位於背景的記憶體中。系統並未刪除該活動。使用者可能會返回應用程式,因此 Android 會保留您的活動資源。
- 使用最近使用畫面返回應用程式。請注意,活動會在 Logcat 中使用
onRestart()
和onStart()
重新啟動,然後使用onResume()
重新啟用。
2020-10-16 10:42:18.144 22064-22064/com.example.android.dessertclicker D/MainActivity: onRestart Called 2020-10-16 10:42:18.158 22064-22064/com.example.android.dessertclicker D/MainActivity: onStart Called 2020-10-16 10:42:18.158 22064-22064/com.example.android.dessertclicker D/MainActivity: onResume Called
當活動返回前景時,就不會再次呼叫 onCreate()
方法。活動物件並未刪除,因此不需要重新建立。系統會呼叫 onRestart()
方法,而不是 onCreate()
。請注意,當活動返回前景時,系統會保留「Desserts Sold」數量。
- 至少啟動一個 DessertClicker 以外的應用程式,使裝置的最近使用畫面中含有多個應用程式。
- 開啟最近使用畫面,並開啟其他最近活動。接著返回最近使用的應用程式,並將 DessertClicker 移回前景。
請注意,此處 Logcat 中顯示的回呼與按下主畫面按鈕時相同。應用程式進入背景時,系統會呼叫 onPause()
和 onStop()
,並在應用程式返回時呼叫 onRestart()
、onStart()
和 onResume()
。
當應用程式停止並移至背景,或應用程式返回前景並再次啟動時,系統就會呼叫這些方法。如果遇到這類情況,而您必須在應用程式中執行工作,請覆寫相關的生命週期回呼方法。
那麼,onRestart()
呢?onRestart()
方法與 onCreate()
類似。在活動顯示之前,系統會呼叫 onCreate()
或 onRestart()
。第一次僅會呼叫 onCreate()
方法,onRestart()
則在後續呼叫。onRestart()
方法是放置程式碼的地方,只有在活動為「非」初次啟動時才會呼叫該程式碼。
用途 3:部分隱藏活動
您已經瞭解應用程式啟動且呼叫 onStart()
時,螢幕上會顯示該應用程式。重新啟用應用程式並呼叫 onResume()
時,應用程式會取得使用者焦點,即使用者可與應用程式互動。應用程式完全顯示在畫面上,且有使用者焦點時,這個生命週期部分便是「互動」生命週期。
應用程式進入背景時,焦點會於 onPause()
後消失,且應用程式在 onStop()
後也不再顯示。
焦點和可見度之間的差異非常重要,因為活動在畫面上可以部分顯示,但沒有使用者焦點。在此步驟中,可查看部分顯示,但沒有使用者焦點的活動。
- 在 DessertClicker 應用程式執行時,按一下畫面右上方的「Share」按鈕。
分享活動會顯示在畫面的下半部,但活動仍會在上半部顯示。
- 檢查 Logcat,且注意僅呼叫
onPause()
。
2020-10-16 11:00:53.857 22064-22064/com.example.android.dessertclicker D/MainActivity: onPause Called
在此用途中,系統不會呼叫 onStop()
,因為活動仍部分顯示。不過,這類活動沒有使用者焦點,且使用者無法與其互動,因為位於前景的「分享」活動具有使用者焦點。
為什麼這個差異很重要?僅使用 onPause()
的干擾通常只會持續一小段時間,然後使用者便會返回活動或前往其他活動或應用程式。一般而言,您需要持續更新 UI,應用程式的其餘部分便不會停止運作。
在 onPause()
中執行的程式碼會使其他內容無法顯示,因此請將程式碼保存在輕量 onPause()
中。例如,如果有來電,onPause()
中的程式碼可能會延遲來電通知。
- 按一下分享對話方塊以外的地方,返回應用程式,並留意系統已呼叫
onResume()
。
onResume()
和 onPause()
都必須與焦點有關。活動有焦點時,系統會呼叫 onResume()
方法,而活動失去聚焦時,就會呼叫 onPause()
。
5. 探索設定變更
管理活動生命週期的另一個用例也十分重要:設定變更對活動生命週期的影響。
設定變更於裝置狀態改變時發生,因此基本上,使系統解決變更的最簡單方法就是完全關閉,然後重新建立活動。舉例來說,若使用者變更裝置語言,您必須調整整個版面配置,以配合不同的文字方向和字串長度。如果使用者將裝置插入座架或新增實體鍵盤,應用程式版面配置可能必須採用不同的顯示大小或版面配置。如果裝置螢幕方向改變 (例如將裝置從直向轉為橫向或向後旋轉),您可能需要根據新的螢幕方向變更版面配置。讓我們看看應用程式在這個情境中的行為。
裝置旋轉時資料遺失
- 編譯並執行應用程式,然後開啟 Logcat。
- 將裝置或模擬器旋轉至橫向模式。您可以使用旋轉按鈕或
Control
方向鍵 (在 Mac 上為Command
和方向鍵),將模擬器向左或向右旋轉。 - 檢查 Logcat 中的輸出內容。在
MainActivity
上篩選輸出內容。
2020-10-16 11:03:09.618 23206-23206/com.example.android.dessertclicker D/MainActivity: onCreate Called 2020-10-16 11:03:09.806 23206-23206/com.example.android.dessertclicker D/MainActivity: onStart Called 2020-10-16 11:03:09.808 23206-23206/com.example.android.dessertclicker D/MainActivity: onResume Called 2020-10-16 11:03:24.488 23206-23206/com.example.android.dessertclicker D/MainActivity: onPause Called 2020-10-16 11:03:24.490 23206-23206/com.example.android.dessertclicker D/MainActivity: onStop Called 2020-10-16 11:03:24.493 23206-23206/com.example.android.dessertclicker D/MainActivity: onDestroy Called 2020-10-16 11:03:24.520 23206-23206/com.example.android.dessertclicker D/MainActivity: onCreate Called 2020-10-16 11:03:24.569 23206-23206/com.example.android.dessertclicker D/MainActivity: onStart Called
請注意,當裝置或模擬器旋轉螢幕時,系統會呼叫所有生命週期回呼來關閉活動。接著,當您重新建立活動時,系統會呼叫所有生命週期回呼來啟動活動。
- 裝置旋轉且關閉並重新建立活動後,活動就會以預設值啟動,也就是將甜點售出數量和收益重設為零。
使用 onSaveInstanceState() 儲存套件資料
onSaveInstanceState()
方法為回呼,可用於在 Activity
刪除時儲存您需要的任何資料。在生命週期回呼圖中,系統會在活動停止後呼叫 onSaveInstanceState()
。每當應用程式進入背景時就會呼叫此方法。
您可以將 onSaveInstanceState()
呼叫視為安全防護措施;此呼叫會在活動離開前景時,將少量資訊儲存至套件。系統會在此時儲存資料,因為若等待直到應用程式關閉,系統可能會有資源壓力。
每次儲存資料可確保套件中的已更新資料能夠視需要還原。
- 在
MainActivity
中覆寫onSaveInstanceState()
回呼,並新增記錄陳述式。
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Log.d(TAG, "onSaveInstanceState Called")
}
- 編譯並執行應用程式,然後按一下「Home」按鈕,讓應用程式進入背景。請注意,
onSaveInstanceState()
回呼緊接在onPause()
和onStop()
之後:
2020-10-16 11:05:21.726 23415-23415/com.example.android.dessertclicker D/MainActivity: onPause Called 2020-10-16 11:05:22.382 23415-23415/com.example.android.dessertclicker D/MainActivity: onStop Called 2020-10-16 11:05:22.393 23415-23415/com.example.android.dessertclicker D/MainActivity: onSaveInstanceState Called
- 在檔案頂端的類別定義前方,加入以下常數:
const val KEY_REVENUE = "revenue_key"
const val KEY_DESSERT_SOLD = "dessert_sold_key"
您將使用這些鍵儲存與擷取執行個體狀態套件中的資料。
- 向下捲動至
onSaveInstanceState()
,並注意outState
參數為Bundle
類型。Bundle
是一系列鍵/值組合,其中鍵一律為字串。您可以將簡易的資料 (例如Int
和Boolean
值) 加入套件中。由於系統會將此套件儲存在記憶體中,因此最佳做法是讓套件中的資料保持精簡。此套件的大小有限,但尺寸會因裝置而異。如果儲存過多資料,應用程式可能會因為TransactionTooLargeException
錯誤而異常終止。 - 在
onSaveInstanceState()
中,使用putInt()
方法將revenue
值 (整數) 加入套件中:
outState.putInt(KEY_REVENUE, revenue)
putInt()
方法 (以及 Bundle
類別中的類似方法,例如 putFloat()
和 putString()
) 使用兩個引數:鍵字串 (KEY_REVENUE
常數),以及要儲存的實際值。
- 按照相同的甜點售出數量重複上述步驟:
outState.putInt(KEY_DESSERT_SOLD, dessertsSold)
使用 onCreate() 還原套件資料
您可以在 onCreate(Bundle)
或 onRestoreInstanceState(Bundle)
中還原活動狀態 (由 onSaveInstanceState()
方法填入的 Bundle
會傳遞至這兩個生命週期回呼方法)。
- 向上捲動至
onCreate()
,並檢查方法簽章:
override fun onCreate(savedInstanceState: Bundle?) {
請注意,每次呼叫時,onCreate()
都會獲得一個 Bundle
。當您的活動因程序關閉而重新啟動時,您儲存的套件會傳遞至 onCreate()
。如果您的活動重新啟動,onCreate()
中的此 Bundle
為 null
。因此,如果套件並非 null
,表示您是從先前的已知點「重新建立」活動。
- 在設定
binding
變數後,將此程式碼新增至onCreate()
:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}
null
的測試可判斷套件中是否含有資料,或套件是否為 null
,以讓您瞭解應用程式是否已重新啟動,或在關閉後重新建立。這項測試是從套件還原資料的常見模式。
請注意,您在此處使用的鍵 (KEY_REVENUE
) 與您在 putInt()
中使用的鍵相同。為確保每次都使用相同的鍵,最佳做法是將這些鍵定義為常數。請使用 getInt()
將資料移出套件,如同使用 putInt()
將資料放入套件一樣。getInt()
方法使用兩個引數:
- 可做為鍵的字串,例如收益值的
"key_revenue"
。 - 預設值,假如套件中沒有該鍵的任何值。
系統會將從套件中取得的整數指派給 revenue
變數,且 UI 會使用該值。
- 新增
getInt()
方法,還原收益和甜點售出數量。
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
}
- 編譯並執行應用程式。按下杯子蛋糕至少五次,直到切換成甜甜圈為止。
- 旋轉裝置。請注意,應用程式現在可以顯示正確的收益,以及套件中的甜點售出值。另請注意,甜點已恢復為杯子蛋糕。
您還需要再執行一項操作,確保應用程式從關閉狀態完全恢復成原先的狀態。
- 在
MainActivity
中檢查showCurrentDessert()
方法。請注意,此方法會根據目前的甜點售出數量和allDesserts
變數中的甜點清單,判斷要在活動中顯示的甜點圖片。
for (dessert in allDesserts) {
if (dessertsSold >= dessert.startProductionAmount) {
newDessert = dessert
}
else break
}
此方法仰賴甜點的售出數量來選擇正確的圖片。因此,您無須採取任何行動,以儲存 onSaveInstanceState()
中套件的圖片參照。您已在該套件中儲存甜點售出數量。
- 在
onCreate()
中,從套件還原狀態的區塊呼叫showCurrentDessert()
:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
showCurrentDessert()
}
- 編譯並執行應用程式,然後旋轉畫面。請注意,甜點售出值、總收益和甜點圖片已順利還原。
6. 摘要
活動生命週期
- 活動生命週期是一組活動轉移的狀態。活動生命週期是從首次建立時開始,並在活動刪除時結束。
- 使用者在不同活動之間及應用程式內外瀏覽時,每個活動會在活動生命週期中的狀態間切換。
- 活動生命週期中的每個狀態都有相應的回呼方法,可在
Activity
類別中覆寫。生命週期方法的核心組合如下:onCreate()
onStart()
onPause()
onRestart()
onResume()
onStop()
onDestroy()
- 如要新增在活動轉換為生命週期狀態時發生的行為,請覆寫狀態的回呼方法。
- 如要在 Android Studio 中為類別新增架構覆寫方法,請依序選取「Code」>「Override Methods」,或按下
Control+o
。
使用記錄檔進行記錄
- Android Logging API (尤其是
Log
類別),可讓您編寫在 Android Studio 的 Logcat 中顯示的簡短訊息。 - 請使用
Log.d()
編寫偵錯訊息。這個方法使用兩個引數:記錄標記,通常是類別名稱,以及記錄訊息,此為簡短字串。 - 使用 Android Studio 中的「Logcat」視窗檢視系統記錄,包括您編寫的訊息。
保留活動狀態
- 應用程式進入背景時,只要呼叫
onStop()
後,即可將應用程式資料儲存至套件中。系統會自動儲存部分應用程式資料 (例如EditText
的內容)。 - 套件是
Bundle
執行個體,也是鍵和值的集合。鍵一律是字串。 - 使用
onSaveInstanceState()
回呼將其他資料儲存至您要保留的套件 (即使應用程式會自動關閉)。如要將資料放入套件,請使用開頭為put
的套件方法,例如putInt()
。 - 您可以從
onRestoreInstanceState()
方法中的套件,或更常見的onCreate()
取回資料。onCreate()
方法的savedInstanceState
參數可用來保存套件。 - 如果
savedInstanceState
變數為null
,表示活動是在沒有狀態套件的情況下開啟動,且沒有可擷取的狀態資料。 - 如要透過鍵從套件擷取資料,請使用以
get
開頭的Bundle
方法,例如getInt()
。
設定變更
- 設定變更於裝置狀態改變時發生,因此基本上,使系統解決變更的最簡單方法就是刪除,然後重新建立活動。
- 最常見的設定變更範例為,使用者將裝置從直向轉為橫向模式,或從橫向轉為直向模式時。裝置語言變更或外接硬體鍵盤時,也可能會發生設定變更。
- 發生設定變更時,Android 會叫用所有活動生命週期的關閉回呼。接著,Android 會從頭重新啟動活動,並執行所有生命週期啟動回呼。
- Android 因設定變更而關閉應用程式時,Android 會以
onCreate()
可用的狀態套件來重新啟動活動。 - 如同程序中斷,請將應用程式的狀態儲存到
onSaveInstanceState()
的套件中。