本篇文章主要介紹以下幾個知識點:
- Activity 的生命周期全面分析;
- Activity 的啟動模式。
1.1 Activity 的生命周期全面分析
典型情況下的生命周期,指在有用戶參與的情況下,Activity 所經過的生命周期的改變。
異常情況下的生命周期,指 Activity 被系統回收或由于設備的 Configuration
改變導致 Activity 被銷毀重建。
1.1.1 典型情況下的生命周期分析
(1)針對一個特定的 Activity,首次啟動,回調如下:onCreate
->onStart
-> onResume
。
(2)當用戶打開新的 Activity 或切換到桌面時,回調如下:onPause
-> onStop
。(注:若新 Activity 采用了透明主題,則當前 Activity 不會回調 onStop
)
(3)當用戶再次回到原 Activity 時,回調如下:onRestart
-> onStart
-> onResume
。
(4)當用戶按 back 鍵回退時,回調如下:onPause
-> onStop
-> onDestroy
。
(5)當 Activity 被系統回收后再次打開,生命周期方法回調過程和(1)一樣。(注:只是生命周期方法一樣,不代表所有過程都一樣)
(6)整個生命周期:onCreate
和 onDestroy
是配對的(標識著 Activity 的創建和銷毀,只調用一次)。
??Activity 是否可見:onStart
和 onStop
是配對的(可能被調用多次);
??Activity 是否在前臺:onResume
和 onPause
是配對的(可能被調用多次)。
問題 1:onStart
和 onResume
、onPause
和 onStop
從描述上看差不多,對我們來說有什么實質的不同呢?
??答:這兩配對的回調具有不同的意義,onStart
和 onStop
是根據 Activity 是否可見來回調的,而 onResume
和 onPause
是根據 Activity 是否位于前臺來回調的,除此之外,在實際使用中無其他明顯區別。
問題2:假設當前Activity為A,若這時用戶打開一個新Activity B,那么B的 onResume
和 A 的 onPause
哪個先執行呢?
??答:當新啟動一個 Activity 時,舊 Activity 的 onPause
會先執行,然后才會啟動新的 Activity。(注:onPause
和 onStop
都不能執行耗時的操作,尤其是 onPause
)
1.1.2 異常情況下的生命周期分析
- 情況 1:資源相關的系統配置發生改變導致 Activity 被殺死并重新創建
在默認情況下,若 Activity 不做特殊處理,當系統配置發生改變后,Activity 就會被銷毀并重新創建,其生命周期如圖:
當系統配置發生改變后,Activity 會被銷毀,其 onPause
、onStop
、onDestroy
均會被調用,同時由于 Activity 是在異常情況下終止的,系統會調用 onSaveInstanceState
來保存當前Activity 的狀態。
方法 onSaveInstanceState
的調用時機是在 onStop
之前(和 onPause
無既定的時序關系),并且只會在 Activity 被異常終止的情況下回調。
當 Activity 被重新創建后,系統會調用 onRestoreInstanceState
,并把 Activity 銷毀時 onSaveInstanceState
方法保存的 Bundle 對象作為參數同時傳遞給 onRestoreInstanceState
和 onCreate
方法。
因此,可以通過 onRestoreInstanceState
和 onCreate
方法來判斷 Activity 是否被重建了,若被重建了,可取出之前保存的數據并恢復。(onRestoreInstanceState
在 onStart 之后調用)
關于保存和恢復 View 層次結構,其工作流程為:首先 Activity 被意外終止時,Activity 會調用 onSaveInstanceState
去保存數據,然后 Activity 會委托 Window 去保存數據,接著 Window 再委托它上面的頂級容器去保存數據。
- 情況 2:資源內存不足導致低優先級的 Activity 被殺死
Activity 按照優先級從高到低,可以分為如下三種:
(1)前臺 Activity——正在和用戶交互的 Activity,優先級最高。
(2)可見但非前臺 Activity —— 如 Activity 中彈出一個對話框,導致 Activity 可見但位于后臺無法和用戶直接交互。
(3)后臺 Activity——已經被暫停的 Activity,如執行了 onStop
,優先級最低。
當系統內存不足時,系統會按上述優先級殺死目標 Activity 所在的進程,并在后續通過 onSaveInstanceState
和 onRestoreInstanceState
來存儲和恢復數數據。
系統配置中有很多內容,當某項內容改變后,若不想系統重新創建 Activity,可以給 Activity 指定 configChanges
屬性。
如給 configChanges
屬性添加 android:configChanges="orientation"
可避免 Activity 在屏幕旋轉時重新創建。
若想指定多個值,可用“|”連接起來,如 android:configChanges="orientation|keyboardHidden"
。
系統配置中所含的項目很多,如下:
上表中項目很多,但常用的只有 locale
、orientation
和 keyboardHidden
這三個。
值得注意的是 ,screenSize
和 smallestScreenSize
比較特殊,它們的行為和編譯選項有關,和運行環境無關。
1.2 Activity 的啟動模式
1.2.1 Activity 的 LaunchMode
(1)standard:標準模式。
系統的默認模式,每次啟動一個 Activity 都會重新創建一個新的實例,不管這個實例是否已存在。
值得注意的是,用 ApplicationContext
去啟動 standard 模式的 Activity 時會報錯,如以下代碼:
tv_text.setOnClickListener {
// 點擊跳轉到 KotlinActivity
val intent = Intent()
intent.setClass(applicationContext, KotlinActivity::class.java)
applicationContext.startActivity(intent)
}
運行會報如下錯誤:
這是因為 standard 模式的 Activity 默認會進入啟動它的 Activity 所屬的任務棧中,但由于非 Activity 類型的 Context
(如 ApplicationContext
)并無所謂的任務棧,從而報錯。
解決上面問題的方法是為待啟動 Activity 指定 FLAG_ACTIVITY_NEW_TASK
標記位,這樣啟動時就會為它創建一個新的任務棧(此時待啟動 Activity 是以 singleTask
模式啟動的)。
(2)singleTop:棧頂復用模式。
此模式下,若新 Activity 已經位于任務棧的棧頂,則此 Activity 不會被重新創建,同時它的 onNewIntent
方法會被回調,通過此方法的參數可以取出當前請求的信息。
值得注意的是,這個 Activity 的 onCreate
、onStart
不會被系統調用,因為它并沒有發生改變。
(3)singleTask:棧內復用模式。
一種單實例模式,此模式下,只要 Activity 在一個棧中存在,那么多次啟動此 Activity 都不會重新創建實例,和 singleTop
一樣,系統也會回調其 onNewIntent
。
(4)singleInstance:單實例模式。
一種加強的 singleTask
模式,具有 singleTask
模式的所有特性,并且具有此種模式的 Activity 只能單獨地位于一個任務棧中。
給 Activity 指定啟動模式有兩種方法:
1. 通過 AndroidMenifest
為 Activity 指定啟動模式
2. 通過在 Intent 中設置標志位為 Activity 指定啟動模式。
二者區別在于:
(1) 優先級上,方式2的優先級高于方式1,當兩種同時存在時,以方式2為準;
(2) 限定范圍不同,比如,方式1無法直接為 Activity 設定 FLAG_ACTIVITY_CLEAR_TOP
標識,而方式2無法為 Activity 指定 singleInstance
模式。
1.2.2 Activity 的 Flags
Activity 的 Flags 有很多,有的標記位可以設定 Activity 的啟動模式,有的會影響 Activity 的運行狀態等。常用的如下:
- FLAG_ACTIVITY_NEW_TASK
其作用是為 Activity 指定 singleTask
啟動模式,效果和在 XML 中指定該模式相同。
- FLAG_ACTIVITY_SINGLE_TOP
其作用是為 Activity 指定 singleTop
啟動模式,效果和在 XML 中指定該模式相同。
- FLAG_ACTIVITY_CLEAR_TOP
具有此標記位的 Activity 啟動時,在同一個任務棧中所有位于它上面的 Activity 都要出棧。
此模式一般需要和 FLAG_ACTIVITY_NEW_TASK
配合使用,若被啟動Activity 的實例已存在,則系統會調用它的 onNewIntent
。
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有此標記的 Activity 不會出現在歷史 Activity 的列表中(用于某些情況不希望用戶通過歷史列表回到 Activity 時)。它等同于在 XML 中指定 Activity 的屬性 android:excludeFromRecents="true"
。
本篇文章就介紹到這。