Activity 之生命周期
本文內容:
1. Activity 介紹
2. Activity 的生命周期
2.1 生命周期圖
2.2 常見情況下生命周期的回調
2.3 關於生命周期常見問題
2.4 異常狀態下活動的生命周期
2.4.1 資源配置改變導致 Activity 重建
2.4.2 低優先級 Activity 由於內存不足被殺死
2.5 異常情況下的處理
2.5.1 數據保存
2.5.2 防止重建
3. 關於 Activity 的不常用的回調方法
3.1 onPostCreate()
3.2 onPostResume()
3.3 onContentChanged()
3.4 onUserInteraction()
3.5 onUserLeaveHint()
1. Activity 介紹
Activity 是 Android 的四大組件之一,主要用於提供窗口與用戶進行交互。
2. Activity 的生命周期
2.1 生命周期圖
官網的 Activity 的生命周期圖:
解釋圖中個方法的作用:
生命周期方法 | 作用 | 說明 |
---|---|---|
onCreate | 表示 Activity 正在被創建 | activity 被創建時調用,一般在這個方法中進行活動的初始化工作,如設置布局工作、加載數據、綁定控件等。 |
onRestart | 表示 Activity 正在重新啟動 | 這個回調代表了 Activity 由完全不可見重新變為可見的過程,當 Activity 經歷了 onStop() 回調變為完全不可見后,如果用戶返回原 Activity,便會觸發該回調,並且緊接着會觸發 onStart() 來使活動重新可見。 |
onStart | 表示 Activity 正在被啟動 | 經歷該回調后,Activity 由不可見變為可見,但此時處於后台可見,還不能和用戶進行交互。 |
onResume | 表示 Activity 已經可見 | 已經可見的 Activity 從后台來到前台,可以和用戶進行交互。 |
onPause | 表示 Activity 正在停止 | 當用戶啟動了新的 Activity ,原來的 Activity 不再處於前台,也無法與用戶進行交互,並且緊接着就會調用 onStop() 方法,但如果用戶這時立刻按返回鍵回到原 Activity ,就會調用 onResume() 方法讓活動重新回到前台。而且在官方文檔中給出了說明,不允許在 onPause() 方法中執行耗時操作,因為這會影響到新 Activity 的啟動。 |
onStop | 表示 Activity 即將停止 | 這個回調代表了 Activity 由可見變為完全不可見,在這里可以進行一些稍微重量級的操作。需要注意的是,處於 onPause() 和 onStop() 回調后的 Activity 優先級很低,當有優先級更高的應用需要內存時,該應用就會被殺死,那么當再次返回原 Activity 的時候,會重新調用 Activity 的onCreate()方法。 |
onDestroy | 表示 Activity 即將被銷毀 | 來到了這個回調,說明 Activity 即將被銷毀,應該將資源的回收和釋放工作在該方法中執行。 |
2.2 常見情況下生命周期的回調
(A 與 B 表示不同的 Activity )
情況 | 回調 |
---|---|
第一次啟動 | onCreate() -> onStart() -> onResume() |
從 A 跳轉到 B | A_onPause() -> B_onCreate() -> B_onStart() -> B_onResume() -> A_onStop() |
從 B 再次回到 A | B_onPause() -> A_onRestart() -> A_onStart() -> A_onResume() -> B_onStop() |
用戶按 home 鍵 | onPause() -> onStop() |
按 home 鍵后回到應用 | onRestart() -> onStart() -> onResume() |
用戶按電源鍵屏保 | onPause() -> onStop() |
用戶按電源鍵亮屏 | onRestart() -> onStart() -> onResume() |
用戶按 back 鍵回退 | onPause() -> onStop() -> onDestroy() |
表中生命周期的驗證可以在 Activity關於生命周期一些問題的實踐驗證 文章中查看。
2.3 關於生命周期常見問題
問題 | 回調 |
---|---|
由活動 A 啟動活動 B時,活動 A 的 onPause() 與 活動 B 的 onResume() 哪一個先執行? | 活動 A 的 onPause() 先執行,活動 B 的 onResume() 方法后執行 |
標准 Dialog 是否會對生命周期產生影響 | 沒有影響 |
全屏 Dialog 是否會對生命周期產生影響 | 沒有影響 |
主題為 Dialog 的 Activity 是否會對生命周期產生影響 | 有影響,與跳轉 Activity 一樣 |
關於生命周期的常見問題的驗證可以在 Activity關於生命周期一些問題的實踐驗證 文章中查看。
2.4 異常狀態下活動的生命周期
當 Activity 在運行過程中發生一些情況時,生命周期流程也會發生變化。常見的異常情況有兩種,一種是資源配置改變;另一是內存不足導致生命周期流程發生變化。
2.4.1 資源配置改變導致 Activity 重建
資源配置最常見的情況就是橫豎屏切換導致資源的變化,當程序啟動時,會根據不同的配置加載不同的資源,例如橫豎屏兩個狀態對應着兩張不同的資源圖片。如果在使用過程中屏幕突然旋轉,那么 Activity 就會因為系統配置發生改變而銷毀重建,加載合適的資源。
2.4.2 低優先級 Activity 由於內存不足被殺死
后台可以同時運行多個任務,當設備的內存空間不足時,系統為了保證用戶的體驗,會按照進程優先級將一些低優先級的進程殺死以會回收內存資源,后台 Activity 就有可能會被銷毀。
系統回收進程的優先級:
(1) 前台進程
持有用戶正在交互的 Activty,即生命周期處於 onResume 狀態的活動。
該進程有綁定到正在交互的 Activity 的 service 或前台 service。
(2) 可見進程
這種進程雖然不在前台,但是仍然可見。
該進程持有的 Activity 執行了 onPause 但未執行 onStop 。例如原活動啟動了一個 dialog 主題的 Activity,但此時原活動並非完全不可見。
該進程有 service 綁定到可見的或前台 Activity。
(3)服務進程
進程中持有一個 service,同時不屬於上面兩種情況。
(4)后台進程
不屬於上面三種情況,但進程持有一個不可見的 Activity,即執行了 onStop 但未執行 onDestory 的狀態。
(5)空進程
不包含任何活躍的應用組件,作用是加快下次啟動這個進程中組件所需要的時間,優先級低。
2.5 異常情況下的處理
在發生異常情況后,用戶再次回到 Activity,原 Activity 會重新建立,原已有的數據就會丟失,比如用戶操作改變了一些屬性值,重建之后用戶就看不到之前操作的結果,在異常的情況下如何給用戶帶來好的體驗,有兩種辦法。
2.5.1 數據保存
第一種就是系統提供的 onSaveInstanceState 和 onRestoreInstanceState 方法,onSaveInstanceState 方法會在 Activity 異常銷毀之前調用,用來保存需要保存的數據,onRestoreInstanceState 方法在 Activity 重建之后獲取保存的數據。
在活動異常銷毀之前,系統會調用 onSaveInstanceState,可以在 Bundle 類型的參數中保存想要的信息,之后這個 Bundle 對象會作為參數傳遞給 onRestoreInstanceState 和 onCreate 方法,這樣在重新創建時就可以獲取數據了。
關於 onSaveInstanceState 與 onRestoreInstanceState 方法需要注意的一些問題:
1. onSaveInstanceState 方法的調用時機是在 onStop 之前,與 onPause 沒有固定的時序關系。而 onRestoreInstanceState 方法則是在 onStart 之后調用。
2. 正常情況下的活動銷毀並不會調用這兩個方法,只有當活動異常銷毀並且有機會重現展示的時候才會進行調用,除了資源配置的改變外,activity 因內存不足被銷毀也是通過這兩個方法保存數據。
3. 在 onRestoreInstanceState 和 onCreate 都可以進行數據恢復工作,但是根據官方文檔建議采用在 onRestoreInstanceState 中去恢復。
4. 在 onSaveInstanceState 和 onRestoreInstanceState 這兩個方法中,系統會默認為我們進行一定的恢復工作,具體地講,默認實現會為布局中的每個 View 調用相應的 onSaveInstanceState() 方法,讓每個視圖都能提供有關自身的應保存信息。Android 框架中幾乎每個小部件都會根據需要實現此方法,以便在重建 Activity 時自動保存和恢復付 UI 所做的任何可見更改。例如 EditText 中的文本信息、ListView 中的滾動位置等。也可以通過 android:saveEnabled 屬性設置為 “false” 或通過調用 setSaveEnabled() 方法顯式阻止布局內的視圖保存其狀態,通常不會將該屬性停用,除非想要以不同方式恢復 Activity IU 的狀態。
5. onSveInstanceState() 常見的觸發場景有:橫豎屏切換、按下電源鍵、按下菜單鍵、切換到別的 Activity 等;onRestoreInstanceState() 常見的觸發場景有:橫豎屏切換、切換語言等等。
2.5.2 防止重建
在默認情況下,資源配置改變會導致活動的重新創建,但是可以通過對活動的 android:configChanges 屬性的設置使活動防止重新被創建。
android:configChanges 屬性值
屬性值 | 含義 |
---|---|
mcc | SIM 卡唯一標識IMSI(國際移動用戶標識碼)中的國家代碼,由三位數字組成,中國為:460,這里標識 mcc 代碼發生了變化 |
mnc | SIM 卡唯一標識 IMSI(國際移動用戶標識碼)中的運營商代碼,有兩位數字組成,中國移動 TD 系統為 00 ,中國聯通為 01,電信為 03,此項標識 mnc 發生了改變 |
locale | 設備的本地位置發生了改變,一般指的是切換了系統語言 |
touchscreen | 觸摸屏發生了改變 |
keyboard | 鍵盤類型發生了改變,比如用戶使用了外接鍵盤 |
keyboardHidden | 鍵盤的可訪問性發生了改變,比如用戶調出了鍵盤 |
navigation | 系統導航方式發生了改變 |
screenLayout | 屏幕布局發生了改變,很可能是用戶激活了另外一個顯示設備 |
fontScale | 系統字體縮放比例發生了改變,比如用戶選擇了個新的字號 |
uiMode | 用戶界面模式發生了改變,比如開啟夜間模式 -API8 新添加 |
orientation | 屏幕方向發生改變,比如旋轉了手機屏幕 |
screenSize | 當屏幕尺寸信息發生改變(當編譯選項中的 minSdkVersion 和 targeSdkVersion 均低於 13 時不會導致 Activity 重啟 ) API 13 新添加 |
smallestScreenSize | 設備的物理尺寸發生改變,這個和屏幕方向沒關系,比如切換到外部顯示設備 -API13 新添加 |
layoutDirection | 當布局方向發生改變的時候,正常情況下無法修改布局的 layoutDirection 的屬性 -API17 新添加 |
可以在屬性中聲明多個配置值,方法使用 “|” 字符分割這些配置值。
API 級別 13 或更高版本的應用時,若要避免由於設備方向改變(橫豎屏切換)而導致運行時重啟,則除了 “orientation” 值之外,還必須添加 “screenSize” 值。
當其中一個配置發生變化時,Activity 不會重啟。相反,Activity 會收到對 onConfigurationChanged() 的調用。向此方法傳遞 Configuration 對象指定新設備配置。可以通過讀取 Configuration 中的字段,確定新配置,然后通過更新界面中使用的資源進行適當的更改。
異常狀態下生命周期與異常情況下的處理的的驗證可以在 Activity關於生命周期一些問題的實踐驗證 文章中查看。
3 關於 Activity 的不常用的回調方法
3.1 onPostCreate()
onPostCreate() 方法是指 onCreate() 方法徹底執行完畢的回調。一般我們都沒有實現這個方法,它的作用是在代碼開始運行之前,調用系統做最后的初始化工作。現在知道的做法是使用 ActionBarDrawerToggle 時在屏幕旋轉的時候在 onPostCreate() 中同步下狀態。
3.2 onPostResume()
onPostResume() 與 onPostCreate() 方法類似,onPostResume() 方法在 onResume() 方法徹底執行完畢的回調。 onCreate() 方法中獲取某個 View 的高度和寬度時,返回的值是 0 ,因為這個時候 View 可能還沒初始化好,但是在 onPostResume() 中獲取就不會有問題,因為 onPostResume() 是在 onResume() 徹底執行完畢的回調。
3.3 onContentChanged()
當 Activity 的布局改動時,即 setContentView() 或者 addContentView() 方法執行完畢時就會調用該方法。所以,Activity 中 View 的 findViewById() 方法都可以放到該方法中。
3.4 onUserInteraction()
Activity 無論分發按鍵事件、觸摸事件或者軌跡球事件都會調用 Activity#onUserInteraction()。如果想知道用戶用某種方式和你正在運行的 activity 交互,可以重寫 Activity#onUserInteraction()。所有調用 Activity#onUserLeaveHint() 的回調都會首先回調 Activity#onUserInteraction() 。
Activity 在分發各種事件的時候會調用該方法,注意:啟動另一個 activity ,Activity#onUserInteraction()會被調用兩次,一次是 activity 捕獲到事件,另一次是調用 Activity#onUserLeaveHint() 之前會調用 Activity#onUserInteraction() 。
可以用這個方法來監控用戶有沒有與當前的 Activity 進行交互。
3.5 onUserLeaveHint()
當用戶的操作使一個 activity 准備進入后台時,此方法會像 activity 的生命周期的一部分被調用。例如,當用戶按下 Home 鍵,Activity#onUserLeaveHint() 將會被調用。但是當來電導致 activity 自動占據前台(系統自動切換),Activity#onUserLeaveHint() 將不會被回調。
一般監聽返回鍵,是重寫 onKeyDown() 方法,但是 Home 鍵和 Menu 鍵就不好監聽,但是可以在 onUserLeaveHint() 方法中監聽。