一、生命周期全面分析
Android活動預設運作在目前程序所擁有的棧中,前台可見的活動則在活動棧的最頂部。其他背景活動則在棧的裡面,在正常的情況下(記憶體充足)其他的活動并沒有被回收或者殺死,它們仍然存在于棧中保持着原來的狀态。目前面的活動退出後,後面的活動就會搬到前台使得被使用者可見。如果在非正常情況下(記憶體緊張、按下Home鍵後右啟動其他應用)那麼棧内的非前台Activity就可能被回收,但是當我們傳回到該Activity時它又會被重新構造,并且會通過onSaveInstance和onRestoreInstance加載原來的資料使得它保持之前的狀态呈現給使用者。無論是正常情況,還是非正常情況下,這樣的實作都是基于一個非常重要的機制——生命周期。
1.什麼是生命周期
周期即活動從開始到結束所經曆的各種狀态。生命周期即活動從開始到結束所經曆的各個狀态。從一個狀态到另一個狀态的轉變,從無到有再到無,這樣一個過程中所經曆的狀态就叫做生命周期。
- Acitivity本質上有四種狀态:
- 運作:如果一個活動被移到了前台(活動棧頂部)。
- 暫停:如果一個活動被另一個非全屏的活動所覆寫(比如一個Dialog),那麼該活動就失去了焦點,它将會暫停(但它仍然保留所有的狀态和成員資訊,并且仍然是依附在WindowsManager上),在系統記憶體積極缺乏的時候會将它殺死。
- 停止:如果一個活動被另一個全屏活動完全覆寫,那麼該活動處于停止狀态(狀态和成員資訊會保留,但是Activity已經不再依附于WindowManager了)。同時,在系統缺乏資源的時候會将它殺死(它會比暫停狀态的活動先殺死)。
- 重新開機:如果一個活動在處于停止或者暫停的狀态下,系統記憶體缺乏時會将其結束(finish)或者殺死(kill)。這種非正常情況下,系統在殺死或者結束之前會調用onSaveInstance()方法來儲存資訊,同時,當Activity被移動到前台時,重新啟動該Activity并調用onRestoreInstance()方法加載保留的資訊,以保持原有的狀态。
在上面的四中常有的狀态之間,還有着其他的生命周期來作為不同狀态之間的過度,用于在不同的狀态之間進行轉換,生命周期的具體說明見下。
二、各個生命周期狀态的說明
1.正常情況下的生命周期
- onCreate:與onDestroy配對,表示Activity正在被建立,這是生命周期的第一個方法。在這個方法中可以做一些初始化的工作(加載布局資源、初始化Activity所需要的資料等),耗時的工作在異步線程上完成。
- onRestart:表示Activity正在重新啟動。一般情況下,在目前Activity從不可見重新變為可見的狀态時onRestart就會被調用。這種情形一般是由于使用者的行為所導緻的,比如使用者按下Home鍵切換到桌面或者打開了一個新的Activity(這時目前Activity會暫停,也就是onPause和onStop被執行),接着使用者有回到了這個Activity,就會出現這種情況。
- onStart:與onStop配對,表示Activity正在被啟動,并且即将開始。但是這個時候要注意它與onResume的差別。兩者都表示Activity可見,但是onStart時Activity還正在加載其他内容,正在向我們展示,使用者還無法看到,即無法互動。
- onResume:與onPause配對,表示Activity已經建立完成,并且可以開始活動了,這個時候使用者已經可以看到界面了,并且即将與使用者互動(完成該周期之後便可以響應使用者的互動事件了)。
- onPause:與onResume配對,表示Activity正在暫停,正常情況下,onStop接着就會被調用。在特殊情況下,如果這個時候使用者快速地再回到目前的Activity,那麼onResume會被調用(極端情況)。一般來說,在這個生命周期狀态下,可以做一些存儲資料、停止動畫的工作,但是不能太耗時,如果是由于啟動新的Activity而喚醒的該狀态,那會影響到新Activity的顯示,原因是onPause必須執行完,新的Activity的onResume才會執行。
- onStop:與onStart配對,表示Activity即将停止,可以做一些稍微重量級的回收工作,同樣也不能太耗時(可以比onPause稍微好一點)。
-
onDestroy:與onCreate配對,表示Activity即将被銷毀,這是Activity生命周期的最後一個回調,我們可以做一些回收工作和最終的資源釋放(如Service、BroadReceiver、Map等)。
正常情況下,Activity的常用生命周期就是上面的7個,下圖更加詳細的描述的各種生命周期的切換過程:
這裡要說的是,從上圖我們可以看到一個現象:
onStart與onStop、onResume與onPause是配對的。兩種Activity回到前台的方式,從onPause狀态回到前台會走到onResume狀态,從onStop狀态回到前台會到onStart狀态,這是從是否可見和是否在前台來說的。從是否可見來說,onStart和onStop是配對的;從是否在前台來說,onResume和onPause是配對的。至于為什麼會有他們,在第三點生命周期的使用會說到。
我們來看看正常情況下生命周期的系統日志:
- :: -/com.example.david.lifecircle E/TAG: onCreate() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onStart() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onResume() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onPause() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onRestart() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onStart() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onResume() is invoked!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
2.異常情況下的生命周期
一般正常情況的聲明周期就像上面所說的一樣,但是因為Android本身記憶體或者其他的一些情況會使得Activity不按照正常的生命周期。比如當資源配置發生改變、系統記憶體不足時,Activity就會被殺死。下面分析這兩種常見的情況。
- 1
- 2
-
情況1:資源相關的系統配置發生改變導緻Activity被殺死并重新建立
了解這個問題,我們首先要對系統的資源加載機制有一定了解,不過這裡我不分析系統資源加載機制了(因為我也不怎麼懂)。簡單說明一下,就像是我們把一張圖檔放在drawable目錄之後,就可以通過Resources去擷取這張圖檔。同時為了相容不同的裝置,我們還可能需要在其他的一些目錄放置不同的圖檔,比如 drawable-mdpi、drawable-hdpi等。這樣,當應用程式啟動時,系統就會根據目前裝置的情況去加載合适的Resource資源,比如說橫屏和豎屏的手機會拿到兩張不同的圖檔(設定了landscape或portrait狀态下的圖檔)。
如果說,目前Activity處于豎屏狀态,如果突然旋轉螢幕,由于系統配置發生了改變,在預設情況下,Activity就會被銷毀并重新建立(當然我們也可以組織系統重新建立,具體就在Mainfest中申明android:Configchanges=屬性即可)。
異常情況下的調用流程:
- 調用onSaveInstance儲存目前Activity狀态。注意,它與onPause方法沒有先後之分。
- 調用onStop方法做後續處理。
- 調用onDestroy方法銷毀目前活動。
- 重新onCreate該活動。
- 調用onStart方法之後,再調用onRestoreInstance方法加載儲存的資料。
- 接下來就與正常的一樣了,調用onResume,然後運作。
- :: -/com.example.david.lifecircle E/TAG: onCreate() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onStart() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onResume() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onPause() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onSaveInstanceState() is invoked! Save Text = Save Data
- :: -/com.example.david.lifecircle E/TAG: onCreate() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onStart() is invoked!
- :: -/com.example.david.lifecircle E/TAG: onRestoreInstanceState() is invoked! Recover Text = Save Data
- :: -/com.example.david.lifecircle E/TAG: onResume() is invoked!
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
-
情況2:資源記憶體不足導緻低優先級的Activity被殺死
這種情況不好模拟,但是其資料存儲和恢複過程和情況1完全一緻,這裡簡單的描述一下Activity的優先級情況。Activity的優先級從高到低可以大緻分為一下三種:
(1)前台Activity——正在和使用者互動的Activity,優先級最高。
(2)可見但非前台Activity——比如Activity中彈出了一個對話框,導緻Activity可見但無法和使用者直接互動。
(3)背景Activity——已經被暫停或者停止的Activity,優先級最底。
當系統記憶體不足的時候,系統就會按照上述優先級從低到高來殺死目标Activity。并在後續通過onSaveInstance和onRestoreInstance來存儲和恢複資料。
特别提醒的是:如果一個程序中沒有四大元件(Activity、Service、ContentProvider、BroadCastReceiver)。那麼這個程序就會很快被殺死,是以一些背景工作不适合脫離四大元件而獨立運作在背景中,否則很容易被殺死。一般是将背景工作放入Service中進而保證程序有一定的優先級,這樣才不會被系統輕易殺死。
三、生命周期的使用
1.常見的生命周期有關問題:
- onStart和onResume、onPause和onStop從描述上看來差不多,但是他們為什麼會分開呢?有什麼不同?
- 兩個Activity A和B,從A中啟動B,那麼B的onResume與A的onPause哪個會先執行呢?
- onSaveInstance與onRestoreInstance是任何情況下都可以使用的嘛?所有的儲存資料和恢複的操作都可以在這對方法中執行?
- 如上面所說,如何使得在系統配置放生改變後,Activity不被重新建立呢?
2.解析
1、 onStart和onResume、onPause和onStop這兩對看起來是差不多,而且很多時候都會同時調用onPause、onStop,然後回到onStart、onResume。但是在一些比較特殊的情況下就不一樣了。我們舉兩種情況,
第一種:前台彈出了一個Dialog,那麼這個Dialog的作用隻是提醒使用者或者讓使用者輸入一個資訊等就完畢了,這是一個比較輕量級的任務;
第二種:重新啟動另一個Activity界面,轉到另一個子產品。這時新啟動的Activity就不是一個臨時或者輕量級的任務了。
這兩種情況,第一種一般很快就會傳回目前Activity,不會太耗時;第二種可能會很久,在這段時間内系統可能需要啟動其他的應用,那麼就會産生記憶體緊張的問題。是以,我認為是要區分這兩種情況,才會加入這兩對方法。在第一種情況下,可以在onPause中做一些較輕微的資料存儲,因為一般很快就會回到目前Activity;第二種情況下,适合在onStop中做一些較重量級的存儲。除此之外,我想不到其他的使用了。
2、這個問題可以從源碼中得到解答。不過源碼太複雜,涉及底層太多(AMS、Binder、ActivityStack、ActivityThread等)。不過可以直接調用生命周期,輸出系統日志來得到解答。從下面的日志我們可以看出,的确是要等到A活動的onPause方法之後B才能執行(這裡onCreate沒有輸出日志):
- :: -/com.example.david.lifecircle E/MainActivity: onCreate() is invoked!
- :: -/com.example.david.lifecircle E/MainActivity: onStart() is invoked!
- :: -/com.example.david.lifecircle E/MainActivity: onResume() is invoked!
- :: -/com.example.david.lifecircle E/MainActivity: onPause() is invoked!
- :: -/com.example.david.lifecircle E/SecondActivity: onStart() is invoked!
- :: -/com.example.david.lifecircle E/SecondActivity: onResume() is invoked!
- 1
- 2
- 3
- 4
- 5
- 6
3、 onSaveInstance和onRestoreInstance是隻有Activity異常銷毀的時候才會調用的,是以這裡一般執行的是Activity異常銷毀時需要儲存和恢複的資料;onSaveInstance和onRestoreInstance方法還可以判斷Activity是否被重建,但正常情況下是不會調用的。是以正常情況下,還是應該在onPause和onStop方法中儲存資料。
4、上面提到,我們可以在AndroidMainfest.xml裡,對< activity />增加一個android:configChanges屬性,來指定在哪些配置改變的情況下Activity不需要重建。如下所示:
- 1
我們在AndroidMainfest.xml做如下申明:
- 1
- 2
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.david.lifecircle">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity"></activity>
</application>
</manifest>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
MainActivity中的部分代碼:
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.e("MainActivity","onConfigurationChanged() is invoked!"+newConfig.orientation);
}
@Override
protected void onPause() {
super.onPause();
Log.e("MainActivity","onPause() is invoked!");
}
@Override
protected void onResume() {
super.onResume();
Log.e("MainActivity","onResume() is invoked!");
}
@Override
protected void onStart() {
super.onStart();
Log.e("MainActivity","onStart() is invoked!");
}
@Override
protected void onRestart() {
super.onRestart();
Log.e("MainActivity","onRestart() is invoked!");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
點選螢幕旋轉,然後來看一下系統日志輸出:
- :: -/com.example.david.lifecircle E/MainActivity: onCreate() is invoked!
- :: -/com.example.david.lifecircle E/MainActivity: onStart() is invoked!
- :: -/com.example.david.lifecircle E/MainActivity: onResume() is invoked!
- :: -/com.example.david.lifecircle E/MainActivity: onConfigurationChanged() is invoked!
- :: -/com.example.david.lifecircle E/MainActivity: onConfigurationChanged() is invoked!
- :: -/com.example.david.lifecircle E/MainActivity: onConfigurationChanged() is invoked!
- 1
- 2
- 3
- 4
- 5
- 6
我們發現,螢幕旋轉之後,并沒有重新調用生命周期,說明活動并沒有被重建。configChanges屬性還有許多的值,如:mcc\mnc\local\touchscreen\keyboard等等。
最後用一個實際的例子來說明Activity的各個生命周期。假設有一個程式由2個Activity A和B組成,A是這個程式的啟動界面。當使用者啟動程式時,Process和預設的Task分别被建立,接着A被壓入到目前的Task中,依次執行了 onCreate, onStart, onResume事件被呈現給了使用者;此時使用者選擇A中的某個功能開啟界面B,界面B被壓入目前Task遮蓋住了A,A的onPause事件執行,B的 onCreate, onStart, onResume事件執行,呈現了界面B給使用者;使用者在界面B操作完成後,使用Back鍵回到界面A,界面B不再可見,界面B的onPause, onStop, onDestroy執行,A的onResume事件被執行,呈現界面A給使用者。此時突然來電,界面A的onPause事件被執行,電話接聽界面被呈現給用 戶,使用者接聽完電話後,又按了Home鍵回到桌面,打開另一個程式“聯系人”,添加了聯系人資訊又做了一些其他的操作,此時界面A不再可見,其 onStop事件被執行,但并沒有被銷毀。此後使用者重新從菜單中點選了我們的程式,由于A和其所在的程序和Task并沒有被銷毀,A的onRestart 和onStart事件被執行,接着A的onResume事件被執行,A又被呈現給了使用者。使用者這次使用完後,按Back鍵傳回到桌面,A的 onPause, onStop被執行,随後A的onDestroy被執行,由于目前Task中已經沒有任何Activity,A所在的Process的重要程度被降到很 低,很快A所在的Process被系統結束
常見的例子
情形一、一個單獨的Activity的正常的生命過程是這樣的:onCreate->onStart->onPause->onStop->onDestroy。例如:運作一個Activity,進行了一些簡單操作(不涉及頁面的跳轉等),然後按傳回鍵結束。
情形二、有兩個Activity(a和b),一開始顯示a,然後由a啟動b,然後在由b回到a,這時候a的生命過程應該是怎麼樣的呢(a被b完全遮蓋)?
a經曆的過程為onCreate->onStart->onResume->onPause->onStop->onRestart->onStart->onResume。這個過程說明了圖中,如果Activity完全被其他界面遮擋時,進入背景,并沒有完全銷毀,而是停留在onStop狀态,當再次進入a時,onRestart->onStart->onResume,又重新恢複。
情形三、基本情形同二一樣,不過此時a被b部分遮蓋(比如給b添加個對話框主題 android:theme=”@android:style/Theme.Dialog”)
a經曆的過程是:onCreate->onStart->onResume->onPause->onResume
是以當Activity被部分遮擋時,Activity進入onPause,并沒有進入onStop,從Activity2傳回後,執行了onResume
情形四、 打開程式,啟動a,點選a,啟動AlertDialog,按傳回鍵從AlertDialog傳回。
a經曆的過程是:onCreate->onStart->onResume
當啟動和退出Dialog時,Activity的狀态始終未變,,因為Dialog實際上時一個View,它是屬于某一個Activity的,是以如果Dialog顯示在目前Activity之前時不會影響到Activity的生命周期的。但是如果是其他Activity的Dialog彈出那麼就會觸發onPause()方法的執行。
轉載來源:https://blog.csdn.net/woshimalingyi/article/details/50961380