Android生命周期
Android的生命周期:onCreate() -> onStart() -> onResume() -> onPause() -> onStop() -> onDestroy() 如下圖所示:
- 1.當activity啟動時系統會先調用onCreate(),然后調用onStart(),最后調用**onResume()**方法,activity進入運行狀態。
- 當activity被別的activity 覆蓋在其上時:系統會掉用onPause(),然后當覆蓋在其上的activity會調用**onCreate() -> onStart() -> onResume()后,第一個activity會調用onStop()**方法使activity暫停。
- 當覆蓋在其上的第二個activity關閉返回此activity時,系統會先調用第二個activity的**onPause()方法然后再調用第一個activity的onRestart() -> onStart() -> onResume()方法,進入運行狀態,此時第二個activity才調用onStop() -> onDestroy()**方法關閉。
- 用戶退出當前activity:系統先調用onPause(),然后調用onSotp(),最后調用**onDestroy()**方法,結束當前activity。
如下日志打印 第一個activity MainActivity 打開 SingleTopActivity 在返回到 MainActivity
MainActivity :onPause 方法-------------------------SingleTopActivity :onCreate 方法-------------------------SingleTopActivity :onStart 方法-------------------------SingleTopActivity :onResume 方法-------------------------MainActivity :onSaveInstanceState 方法--------------------MainActivity :onStop 方法-------------------------SingleTopActivity :onPause 方法-------------------------MainActivity :onRestart 方法-------------------------MainActivity :onStart 方法-------------------------MainActivity :onResume 方法-------------------------SingleTopActivity :onStop 方法-------------------------SingleTopActivity :onDestroy 方法-------------------------
復制代碼
-
onRestart():表示activity正在重新啟動,一般情況下是當前activity從不可見重新變成可見狀態時,**onRestart()**就會被調用,這種情況一般是用戶行為導致的,如從其他頁面返回當前頁面時,或者用戶按home鍵切換到桌面在重新打開app。
-
onStart()和onStop():onStart()表示activity可見了,但是還沒有獲取焦點,無法進行交互。onStop()是和onStart()對應的當activity從可見轉不可見是調用。
-
onResume()和onPause():onResume()表示activity已經獲取焦點了,可以進行交互了,onPause()是和onResume()方法對應的表示當前activity失去了焦點,此時可以做一些存儲數據和停止動畫等工作,但是不能太好時,不是會影響到新的activity的顯示,因為只有onPause()執行完了,新的activity才會進入 onCreate() 等方法。
-
onDestroy():onDestroy()表示activity正在銷毀,一般我們是在這進行資源的釋放,以避免內存的泄漏。
注意:
- 如果覆蓋在其上的activity的風格是dialog風格的化,此activity是不會進入**onSotp()**方法,回到此activity時也**不會調用onRestart()和onStart()方法** 會直接調用**onResume()**方法。- 如果activity中彈出dialog對話框的時候,**activity是不會調用onPause()方法**;- 只有舊的activity onPause()方法執行完后,新的activity才啟動
- 在所以情況下,系統在調用了onPause()和onStop()之后都會調用onDestroy(),只有一個例外:當你從onCreate()方法類調用了finish()時,在這種情況下,系統會立刻調用onDestroy(),而不調用任何其他生命周期方法。日志如下:
復制代碼
MainActivity :onPause 方法-------------------------DialogActivity :onCreate 方法-------------------------MainActivity :onResume 方法-------------------------DialogActivity :onDestroy 方法-------------------------
復制代碼
異常情況下的生命周期:比如系統資源配置發生改變以及系統內存不足時,activity就可能被殺死。
- 情況1:資源相關配置方式改變導致activity被殺死并重新創建。 比如當前activity處于豎屏,旋轉屏幕,這時由于activity的系統配置改為了橫屏狀態,在默認情況下,activity就會被銷毀并且重新創建,日志打印如下:
MainActivity :onCreate 方法-------------------------MainActivity :onCreate:MainActivity TaskId:130 hasCode:151566767MainActivity :onStart 方法-------------------------MainActivity :onResume 方法-------------------------MainActivity :onPause 方法-------------------------MainActivity :onSaveInstanceState 方法-------------------------MainActivity :onStop 方法-------------------------MainActivity :onDestroy 方法-------------------------MainActivity :onCreate 方法-------------------------MainActivity :onCreate:MainActivity TaskId:130 hasCode:233659052MainActivity :onStart 方法-------------------------MainActivity :onRestoreInstanceState 方法-------------------------MainActivity :onResume 方法-------------------------MainActivity :onPause 方法-------------------------MainActivity :onSaveInstanceState 方法-------------------------MainActivity :onStop 方法-------------------------MainActivity :onDestroy 方法-------------------------MainActivity :onCreate 方法-------------------------MainActivity :onCreate:MainActivity TaskId:130 hasCode:262962597MainActivity:TaskAffinity:com.hugo.reviewbasicMainActivity :onStart 方法-------------------------MainActivity :onRestoreInstanceState 方法-------------------------MainActivity :onResume 方法-------------------------
復制代碼
在上面這日志中 我們進入MainActivity -> onCreate() -> onStart () -> onResume() 這個時候MainActivity已經在棧頂并獲得焦點了,然后我們旋轉手機屏幕,
這時調用了 onPause() -> onSaveInstanceState() -> onStop() - onDestroy() 方法把當前MainActivity 銷毀了,然后緊接著又重新創建了一個MainActivity -> onCreate() -> onStart() -> onRestoreInstanceState() -> onResume()
這是MainActivity已經重新創建并且是橫屏顯示的,這是我們又旋轉手機屏幕重新豎屏顯示 ,這是調用了 onPause() -> onSaveInstanceState() -> onStop() - onDestroy() 方法把當前橫屏MainActivity 銷毀了,并重新創建了豎屏的MainActivity -> onCreate() -> onStart() -> onRestoreInstanceState() -> onResume()
在這上面的流程我們可以看到 在第一次進入MainActivity 時是每有調用onRestoreInstanceState() 方法的而是在重新創建時才調用了該方法,這個方法是用來做什么的呢?這個方法就是用來在activity被銷毀并重新創建時用來恢復我們保存的數據用的,那我們的數據在哪保存的呢,可以看到日志里每次在銷毀前都有調用 onSaveInstanceState() 方法,這個方法就是用來保存數據用的。
在onSaveInstanceState()方法中系統會傳入Bundle對象用來存儲數據,在重新創建時onRestoreInstanceState()方法系統會傳入在onSaveInstanceState()方法是存儲了數據的Bundle對象,在onRestoreInstanceState()方法里可以在傳入的Bundle對象中獲取存儲的數據進行頁面恢復。
根據日志可以看出 onSaveInstanceState() 總是在 onStop()之前調用,而onRestoreInstanceState() 總是在onStart() 之后調用,而且onRestoreInstanceState()在activity第一次創建時是不會調用的。
-
情況2:在資源不足的情況下導致低優先級的activity被殺死。 這種情況下和前面第一種情況1的數據存儲和恢復是完全一致的,activity按照優先級從高到低可以分為以下三種:
- 前臺activity:正在和用戶交互的activity,優先級最高。 2.可見但是非前臺activity:就是能看見,但是沒有獲取到焦點不能和用戶進行直接交互。 3.后臺activity:已經被暫停的activity,比如執行了onStop()方法,優先級最低。
注意: 必須始終調用 onSaveInstanceState()和onRestoreInstanceState() 的超類實現因為這兩個方法默認實現了保存有關activity視圖層次的狀態信息和恢復視圖層次結構狀態,列如EditText小部件的文本或ListView的滾動位置。而且所有的View都有onSaveInstanceState()和onRestoreInstanceState()這兩個方法。
自行處理配置變更: 我們可以在聲明Activity將自行處理配置變更,這樣就可以阻止系統重啟activity了。 聲明時在AndroidManifest.xml文件中編輯相應的""元素,設置以包含的 android:configChanges 屬性(最常用的值包括“orientation”和“keyboardHidden”,分別用于避免因屏幕方向和可用鍵盤改變而導致的重啟)。我們可以在該屬性中聲明多個匹配值,方法是用“ | ”字符分隔這些配置值。 如下配置:
<activity android:name=".singletop.OtherTopActiivty"
android:configChanges="orientation|screenSize"
android:launchMode="singleTop"/>
復制代碼
現在,當其中一個配置發生改變是OtherTopActiivty不會重啟,但是OtherTopActiivty的onConfigurationChanged()方法會被調用,系統會 傳入Configuration對象指定新的設備配置。我們可以通過讀Configuration中的字段,來確定新配置,進行相應的UI更改。 以下是在onConfigurationChanged()實現檢查當前設備的方向:
override fun onConfigurationChanged(newConfig: Configuration?) {???
super.onConfigurationChanged(newConfig)??
LogUtil.i(TAG,"$ActivityName :onConfigurationChanged 方法-------------------------")?
if(newConfig?.orientation == Configuration.ORIENTATION_LANDSCAPE){?????
LogUtil.i(TAG,"$ActivityName :onConfigurationChanged 橫屏")??
Toast.makeText(this,"橫屏",Toast.LENGTH_SHORT).show()??
}else if(newConfig?.orientation == Configuration.ORIENTATION_PORTRAIT){????
LogUtil.i(TAG,"$ActivityName :onConfigurationChanged 豎屏")???
Toast.makeText(this,"豎屏",Toast.LENGTH_SHORT).show()?
}
}復制代碼
注意:
-
自行處理配置變更可能導致備用資源的使用更為困難,因為系統不會自動應用這些資源。所以只有在我們必須避免activity因配置改變而重啟這種情況下,才考慮采用自行處理配置變更這種方法,而且對于大多數應用并不建議使用此方法。 復制代碼
-
在Android3.0(API 級別13)開始,設備在縱向和橫向之間切換時,“屏幕尺寸”也會發生改變,因此在開發針對API級別13或更高版本的應用時,若要避免由于設備方向的改變而導致運行時重啟,則除了“orientation”值外,還必須添加“screenSize“值。也就是必須聲明android:configChanges="orientation|screenSize"。 復制代碼
-
在聲明有Activity處理配置變更時,我們有責任要為其提供備用資源的所有元素。如聲明了activity處理方向變更,有些圖像是應該橫向和縱向之間切換,則必須在 onConfigurationChanged()方法中將每個資源重新分配給每個元素。 復制代碼
這個例子代碼在這里