生命週期詳細解讀(含部分原始碼)

寒蟬Cicada發表於2019-05-14

前言

對於Activity的生命週期大家都很熟悉,有onCreate、onStart、onResume、onPause、onStop、onDestroy這幾個方法。

本文將講述關於Activity生命週期更深層次的知識(部分內容從原始碼角度解讀),並且講述一下不常被提及,卻十分重要的概念。

生命週期概述

Activity生命週期

幾種生命週期

(1)onCreate:表是Activity正在建立,是生命週期的第一個方法,主要進行載入佈局、初始化Window等操作。

(2)onRestart:表示Activity正在重新啟動,當Activity由不可見變為可見時呼叫。這一行為主要是由使用者觸發(Back或切換應用等操作)。

(3)onStart:表示Activity正在啟動,這時Activity已經可見了,只是還沒有出現在前臺,無法和使用者互動。

(4)onResume:表示Activity已經可見了,這時Activity已經可見了,並且出現在了前臺,可以和使用者進行互動。onStart和onResume都表示Activity已經可見了,但是onResume時,Activity才會顯示到前臺,並且可以進行互動。

(5)onPause:表示Activity正在暫停,後面通常會呼叫onStop方法(部分特例請看下文)。onPause可以做一些資料暫存、動畫關閉等操作,不能進行耗時操作,因為下一個Activity啟動前,必須要當前Activity已經Pause後才能顯示(具體原因請看下文),耗時操作會影響到下一個頁面的顯示。

(6)onStop:表示Activity正在停止,可以進行一些略重量級的回收工作,但也不能太耗時。

(7)onDestroy:表示Activity正在被銷燬,是Activity生命週期的最後一個回撥,可以進行最終的資源釋放(unRegister或unBind)。

常規生命週期切換

(1)新建一個Activity,新Activity的生命週期回撥如下:onCreate->onStart->onResume。 (2)新建一個Activity,原Activity的生命週期回撥如下:onPause->onStop(部分情況下不呼叫)。這裡需要需要注意,當新頁面使用透明、Dialog主題時,原頁面並不會呼叫onPause方法。 (3)當使用者Back時,原Activity的生命週期回撥如下:onRestart->onStart->onResume。 (4)當使用者Back時,當前Activity的生命週期回撥如下:onPause->onStop->onDestroy。 (5)Activity被回收後重新開啟時,生命週期回撥同(1),但是系統會自動進行一些資料、狀態的儲存和恢復工作。

何時生命週期不回撥onStop方法?

當原頁面未被完全覆蓋時,即啟動的頁面為Dialog或透明主題,原頁面不會回撥onStop方法。

設定Dialog主題

Activity

<activity
    android:name=".Main2Activity"
    android:theme="@android:style/Theme.Dialog"/>
複製程式碼

AppCompatActivity

<activity
    android:name=".Main2Activity"
    android:theme="@style/Theme.AppCompat.Dialog"/>
複製程式碼

設定透明主題

Activity

<activity
    android:name=".Main2Activity"
    android:theme="@android:style/Theme.Translucent" />
複製程式碼

AppCompatActivity

<style name="MyTranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowBackground">#00000000</item>
    <item name="android:windowIsTranslucent">true</item>
</style>
複製程式碼
<activity
    android:name=".Main2Activity"
    android:theme="@style/MyTranslucentTheme" />
複製程式碼

例項驗證

依據以上方法設定ActivityB主題,驗證log如下:

2019-05-12 00:30:59.526 4740-4740/com.gcc.demo D/ActivityA: onCreate: 2019-05-12 00:30:59.527 4740-4740/com.gcc.demo D/ActivityA: onStart: 2019-05-12 00:30:59.529 4740-4740/com.gcc.demo D/ActivityA: onResume: 2019-05-12 00:31:01.706 4740-4740/com.gcc.demo D/ActivityA: onClick: +++++++++++++++++++啟動ActivityB 2019-05-12 00:31:01.718 4740-4740/com.gcc.demo D/ActivityA: onPause: 2019-05-12 00:31:01.760 4740-4740/com.gcc.demo D/ActivityB: onCreate: 2019-05-12 00:31:01.761 4740-4740/com.gcc.demo D/ActivityB: onStart: 2019-05-12 00:31:01.766 4740-4740/com.gcc.demo D/ActivityB: onResume:

新頁面的onResume和原頁面的onPause誰先呼叫?

前文說到新頁面呼叫onResume前,原頁面的onPause必須先呼叫,具體原因我們從原始碼中獲取。下面貼一段Android9.0中ActivityStack類的resumeTopActivityInnerLocked方法的原始碼:

Android9.0-ActivityStack-resumeTopActivityInnerLocked方法

If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity to be paused, while at the same time resuming the new resume activity only if the previous activity can't go into Pip since we want to give Pip activities a chance toenter Pip before resuming the next activity.

從上面這段原始碼以及相應註釋可以看出,如果沒有設定RESUME_WHILE_PAUSING這個Flag,那麼新頁面呼叫onResume時,原頁面的onPause必須先呼叫

系統配置發生改變時Activity被重新建立

Activity重建時的生命週期

當系統的配置發生改變時(例如手機橫豎屏切換、系統語言切換等),Activity會被重新建立。此時Activity生命週期和建立時一樣,只不過這時候Activity會呼叫onSaveInstanceStateonRestoreInstanceState這一組方法來進行頁面資料的快取以及恢復(正常啟動Activity時不會觸發該組方法)。

PS:Activity的資料快取與恢復可以看本人另一篇文章 Android如何應對記憶體回收機制

如何避免配置修改後Activity被重建

如果不想系統配置修改後重啟Activity,也可以給configChanges新增不需要重啟的指定配置項。例如,橫豎屏、語言變化時不想重啟Activity,可這樣寫:

<activity
    android:name=".Main2Activity"
    android:configChanges="locale|orientation" />
複製程式碼

PS:configChanges詳細配置請看本人另一篇文章 Android修改系統設定後Activity被重新建立

Activity被系統回收後再次重建

當系統記憶體不足時,Android系統會自動回收一些低優先順序的程式,或者回收一些已被停止的後臺Activity。

Activity優先順序

Activity優先順序如下,由高到低:

  1. 前臺Activity--正在和使用者互動的Activity。高優先順序。
  2. 可見但不可互動Activity--上層頁面為Dialog、透明主題,即已Pause但未Stop的頁面。中優先順序。
  3. 後臺Activity--已Stop的Activity。低優先順序。

Activity重建時的生命週期

Activity被重建時,Activity生命週期和新建時一樣,只不過這時候Activity也會呼叫onSaveInstanceStateonRestoreInstanceState這一組方法來進行頁面資料的快取以及恢復(正常啟動Activity時不會觸發該組方法)。

PS:如何處理記憶體被回收可以看本人另一篇文章 Android如何應對記憶體回收機制

NewIntent狀態下的生命週期

什麼情況下會觸發onNewIntent回撥方法?

這個問題我們從原始碼中求解。下面貼一段Android9.0中Activity類的startActivityIfNeeded方法的頭註釋:

Android9.0-Activity-startActivityIfNeeded註釋

if you are using the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP} flag, or singleTask or singleTop and the activity that handles intent is the same as your currently running activity , then a new instance is not needed.

從註釋中可以看出,在不需要建立一個新的Activity例項時,生命週期會回撥onNewIntent方法。不需要建立新Activity例項主要有這兩種情況: (1)啟動模式為singleTask,且棧內已存在待建立例項; (2)啟動模式為singleTop,且棧頂就是待建立例項。

觸發onNewIntent方法時,Activity生命週期是怎樣的?

在singleTask和singleTop啟動模式下,啟動一個已存在的Activity(PS:只看在棧頂的情況),該Activity的生命週期會如何回撥呢?

回撥的時序應該是onPause->onNewIntent->onResume。

觸發onNewIntent回撥的注意事項

在回撥onNewIntent方法時,需要注意將onNewIntent傳入的intent替換為當前Intent,否則新Intent附加值並不會被傳遞給頁面。需要呼叫如下方法:

setIntent方法

相關文章