前言
Android四大元件使用最多的就是與使用者直接接觸的Activity,而Activity你瞭解多少呢?生命週期、異常生命週期、Activity之間傳值、啟動模式、Flags的使用等,小夥伴們都曉得嗎?今天我們就來了解一下生命週期,生命週期分正常情況下與異常情況下的生命週期。最近在整理Android進階要點,與大家分享,如果有小夥伴想感興趣的話,可以關注我,一起學習交流。我會以kotlin語言舉例子,我們一起來看吧!
正常下生命週期首先大家先看一張圖,我相信大家對這張圖都不陌生吧!
1.生命週期介紹在正常情況下,Activity會經歷過如下生命週期:
(1) onCreate:表示Activity正在被建立,這是生命週期的第一方法。在這個方法中,我們可以做一些初始化工作,比如呼叫setContentView去載入介面佈局資源、初始化Activity所需資料等。
(2)onRestart:表示Activity正在重新啟動。一般情況下,噹噹前Activity從不可見重新變為可見狀態時,onRestart就會被呼叫。這種情形一般使用者行為所導致的,比如使用者按Home鍵切換到桌面或者使用者打開了一個新的Activity,這時當前的Activity就會暫停,也就是onPause和onStop被執行了,接著使用者又回到了這個Activity,就會出現這種情況。
(3)onStart:表示Activity正在被啟動,即將開始,這時Activity已經可見了,但是還沒有出現在前臺,還無法和使用者互動。這個時候其實可以理解為Activity已經顯示出來了,但是我們還看不到。
(4)onResume:表示Activity已經可見了,並且出現在前臺並開始活動。要注意這個和onStart的對比,onStart和onResume都表示Activity已經可見,但是onStart的時候Activity還在後臺,onResume的時候Activity才顯示到前臺。
(5)onPause:表示Activity正在停止,正常情況下,緊接著onStop就會被呼叫。在特殊情況下,如果這個時候快速地再回到當前Activity,那麼onResume就會被呼叫,這種情況屬於極端情況,使用者操作很難重現這一場景。此時可以做一些儲存資料、停止動畫的操作,但是注意不能太耗時,因為這會影響到新Activity的顯示,onPause必須先執行玩,新的Activity的onResume才會執行。
(6)onStop:表示Activity即將停止,可以做一些稍微重量級的回收工作,同樣不能太耗時。
(7)onDestroy:表示Activity即將被銷燬,這是Activity生命週期中的最後一個回撥,在這裡,我們可以做一些回收工作和最終的資源釋放
2.實際情況驗證為了分析上圖,我們來驗證下:
class MainActivity : AppCompatActivity() { private val TAG:String =MainActivity::class.java.simpleName override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Log.d(TAG, "onCreate: ") } override fun onRestart() { super.onRestart() Log.d(TAG, "onRestart: ") } override fun onStart() { super.onStart() Log.d(TAG, "onStart: ") } override fun onResume() { super.onResume() Log.d(TAG, "onResume: ") } override fun onPause() { super.onPause() Log.d(TAG, "onPause: ") } override fun onStop() { super.onStop() Log.d(TAG, "onStop: ") } override fun onDestroy() { super.onDestroy() Log.d(TAG, "onDestroy: ") }}複製程式碼
上面可以看出,我們新建一個MainActivity,然後並把每個生命週期函式都打印出日誌
執行Activity:onCreate()-->onStart()-->onResume()按下Home鍵:onPause()-->onStop()按下Home鍵後,再次開啟Activity:onRestart()-->onStart()-->onResume()按下Back鍵:onPause()-->onStop()-->onDestroy()按下Back鍵後,再次開啟:onCreate()-->onStart()-->onResume()跳轉另一個SecActivity: MainActivity執行:onPause()-->onStop() SecActivity執行:onCreate()-->onStart()-->onResume()按Back返回MainActivity: SecActivity執行:onPause()-->onStop()-->onDestroy() MainActivity執行:onRestart()-->onStart()-->onResume()還有一種情況就是SecActivity設定視窗的背景為透明,這時從MainActivity跳轉到SecActivity生命週期如下: MainActivity執行:onPause()SecActivity執行:onCreate()-->onStart()-->onResume() 這時候按Back鍵後: SecActivity執行:onPause()-->onStop()-->onDestroy() MainActivity執行:onResume()透明主題是一種特殊情況!
異常下生命週期1. 資源相關的系統配置發生改變導致Activity被殺死並重新建立理解這個問題,我們首先要對系統的資源載入機制有一定的瞭解,這裡不詳細分析系統的資源載入機制,只簡單說明一下。拿簡單的圖片來說,當我們把一張圖片放在drawable目錄後,就可以透過Resource去獲取這樣圖片。同時為了相容不同的裝置,我們可能還需要在其他一些目錄放置不同的圖片,比如drawable-mdpi、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi、drawable-xxxhdpi等。這樣,當應用程式啟動時,系統就會根據當前裝置的情況去載入合適的Resource資源,比如說橫屏手機和豎屏手機會拿到兩張不用的圖片(設定了landscape和portrait狀態下的圖片)。比如說當前處於Activity處於豎屏狀態,如果突然旋轉螢幕,由於系統配置發生了改變,在預設情況下,Activity就會被銷燬並且重新建立,當然我們也可以阻止系統重新建立我們的Activity。
在預設情況下,如果我們的Activity不做特殊處理,那麼當系統配置發生改變後,Activity就會被銷燬並重新建立,其生命週期:
當系統配置發生改變後,Activity會被銷燬,其onPause、onStop、onDestroy均會被呼叫,同時由於Activity是在異常情況下終止的,系統會呼叫onSaveInstanceState來儲存當前Activity狀態。這個方法的呼叫時機是在onStop之前,它和onPause沒有既定的時序關係,它既可能在onPause之後呼叫,也可能在onPause之後呼叫,需要強調的一點是,這個方法只會出現在Activity被異常終止的情況下,正常情況下系統不會回撥這個方法。當Activity被重新建立,系統會呼叫onRestoreInstanceState,並且把Activity銷燬時onSaveInstanceState方法所儲存的Bundle物件作為引數同時傳遞給onRestoreInstanceState和onCreate方法。因此,我們可以透過onRestoreInstanceState和onCreate方法來判斷Activity是否被重建了,那麼我們就可以取出之前儲存的資料並恢復,從時序上來說,onRestoreInstanceState的呼叫在時機在onStart之後。
下面舉個例子,來驗證我們自己做的資料儲存和恢復的情況:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Log.d(TAG, "onCreate: ") val test =savedInstanceState?.getString("extra_test") Log.d(TAG, "onCreate: extra_test =$test") } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) Log.d(TAG, "onSaveInstanceState: ") outState.putString("extra_test","test") } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) val test =savedInstanceState.getString("extra_test") Log.d(TAG, "onRestoreInstanceState: extra_test =$test ") }
上面的程式碼很簡單,首先我們在onSaveInstanceState中儲存一個字串,然後在Activity被銷燬並重新建立後,我們再去獲取之前儲存的字串。下面我們看一下執行的日誌:
看上圖可以看出,前半部分時Activity剛啟動的日誌,其引數Bundle savedInstanceState為null。後半部分是旋轉屏幕後的日誌,Activity被銷燬以後呼叫了onSaveInstanceState來儲存資料,重新建立以後在onCreate和onRestoreInstanceState中都能正確地恢復我們之前儲存的字串。針對onSaveInstanceState方法還有一點需要說明,那就是系統只會在Activity即將被銷燬並且有機會重新顯示的情況下會呼叫它。考慮這麼一種情況,當Activity正常銷燬的時候,系統不會呼叫onSaveInstanceState,因為被銷燬的Activity的不可能再次被顯示。這句話不好理解,但是我們可以對比一下旋轉螢幕所造成的Activity異常銷燬,這個過程和正常情況停止Activity時不一樣的,因為旋轉屏幕後,Activity被銷燬的同時會立刻建立新的Activity例項,這個時候Activity有機會再次立刻展示,所以系統要進行資料儲存。這裡可以簡單地這麼理解,系統只會在Activity異常終止的時候才會被呼叫onSaveInstanceState和onRestoreInstanceState來儲存和恢復資料,其他情況不會觸發這個過程。
2. 資源記憶體不足導致低優先順序的Activity被殺死這種情況不好模擬,但是其資料儲存和恢復過程和上個情況完全一致。這裡我們描述一下Activity的優先順序情況,Activity按照優先順序從高到低,可以分為如下三種情況:
前臺Activity :正在和使用者互動的Activity,優先順序最高。可見但非前臺Activity:比如Activity中彈出一個對話方塊,導致Activity可見但是位於後臺無法和使用者直接互動。後臺Activity:已經被暫停的Activity,比如執行了onStop,優先順序最低。當系統記憶體不足時,系統就會按照上述優先順序殺死目標Activity所在的程序,並在後續透過onSaveInstanceState和onRestoreInstanceState來儲存和恢復資料。如果一個程序中沒有四大元件在執行,那麼這個程序很快被系統殺死,因此,一些後臺工作不適合脫離四大元件而獨自執行在後臺中。這樣程序很容易被殺死。比較好的方法是將後臺工作放入Service中從而保證程序有一定的優先順序,這樣就不會輕易地被系統殺死。
configChanges介紹上面我們分析系統異常情況的生命週期和系統的資料儲存和恢復機制,我們知道,當系統配置發生改變後,Activity會重新建立,那麼如果我們不想重新建立呢?可以給Activity制定configChanges屬性。比如我們不想讓Activity在螢幕旋轉的時候重新建立,就可以給configChanges屬性新增orientation和screenSize這兩個值:
android:configChanges="orientation|screenSize"
configChanges的專案和含義專案含義mccSIM卡位- 標識IMSI (國際移動使用者識別碼)中的國家程式碼,由三位數字組成,中國為460。此項 標識mcc程式碼發生了改變mncSIM卡位- 標識IMSI (國際移動使用者識別碼)中的運營商程式碼,由兩位數字組成,中國移動TD系 統為00,中國聯通為01,中國電信為03。此項標識mne發生改變locale裝置的本地位置發生了改變,一般指切換了系統語言touchscreen觸控式螢幕發生了改變,這個很費解,正常情況下無法發生,可以忽略它keyboard鍵盤型別發生了改變,比如使用者使用了外插鍵盤keyboardHidden鍵盤的可訪問性發生了改變,比如使用者調出了鍵盤navigation系統導航方式發生了改變,比如採用了軌跡球導航,這個有點費解,很難發生,可以忽略它screenLayout屏幕布局發生了改變,很可能是使用者激活了另外一個顯示裝置fontScale系統字型縮放比例發生了改變,比如使用者選擇了一個新字號uiMode使用者介面模式發生了改變,比如是否開啟了夜間模式(API8新新增)orientation螢幕方向發生了改變,這個是最常用的,比如旋轉了手機螢幕screenSize當螢幕的尺寸資訊發生了改變,當旋轉裝置螢幕時,螢幕尺寸會發生變化,這個選項比較特殊。它 和編譯選項有關。當編譯選項中的mimSdk Version和angetsd Version均低於13時,此選項不會導致 Activity重啟。否則會導致Activity重啟(AP 13新新增)smallestScreenSize裝置的物理螢幕尺寸發生改變。這個專案和螢幕的方向沒關係。僅僅表示在實際的物理螢幕的尺寸 改變的時候發生,比如使用者切換到了外部的顯示裝置,這個選項和sreenSize - -樣,當編譯選項中的 minSdk Version和targetSdkVersion 均低於13時,此選項不會導致Activity重啟,否則會導致Activity 重啟(API 13新新增)layoutDirection當佈局方向發生變化,這個屬性用的比較少,正常情況下無須修改佈局的layudirction屬性(API 17新新增)