一珊膜、概述
學(xué)習(xí)Activity
生命周期卷玉,首先我們要明白,學(xué)習(xí)它的目的不僅在于要知道有哪些生命周期庶喜,而是在于明白各回掉函數(shù)調(diào)用的時(shí)機(jī)小腊,以便在合適的時(shí)機(jī)進(jìn)行正確的操作,如初始化變量久窟、頁面展示秩冈、數(shù)據(jù)操作、資源回收等斥扛。平時(shí)的工作習(xí)慣都是入问,onCreate(xxx)
初始化,onResume()
注冊(cè)稀颁、拉取數(shù)據(jù)芬失,onPause()
反注冊(cè),onDestroy()
釋放資源匾灶,這篇文章總結(jié)了一些和關(guān)鍵生命周期相關(guān)聯(lián)的一些要點(diǎn)棱烂。
二、金字塔模型
在官方文檔中阶女,把
Activity
的生命周期理解成為一個(gè)金字塔模型颊糜,它是基于下面兩點(diǎn):
- 金字塔的每個(gè)階梯表示
Activity
所處的狀態(tài) - 各回調(diào)函數(shù)則是各狀態(tài)轉(zhuǎn)換過程當(dāng)中所經(jīng)過的路徑
這個(gè)模型中包含了Activity
的六種狀態(tài):
-
Created
:創(chuàng)建完成 -
Started
:可見 -
Resumed
:可見 -
Paused
:部分可見 -
Stopped
:不可見 -
Destroyed
:銷毀
在這六種狀態(tài)當(dāng)中,只有Resumed
秃踩、Paused
衬鱼、Stopped
這幾種狀態(tài)在用戶沒有進(jìn)一步操作時(shí)會(huì)保持在該狀態(tài),而其余的憔杨,都會(huì)在執(zhí)行完相應(yīng)的回調(diào)函數(shù)后快速跳過鸟赫。
三、關(guān)鍵生命周期
3.1 protected void onCreate(Bundle savedInstanceState)
- 該方法被回調(diào)時(shí),意味著一個(gè)新的
Activity
被創(chuàng)建了抛蚤。 - 由于當(dāng)前處于一個(gè)新的
Activity
實(shí)體對(duì)象當(dāng)中台谢,所有的東西都是未初始化的,我們一般需要做的事情包括調(diào)用setContentView
方法設(shè)置該Activity
的布局霉颠,初始化類成員變量对碌。 -
onCreate(xxx)
方法執(zhí)行完之后,Activity
就進(jìn)入了Created
狀態(tài)蒿偎,然而它并不會(huì)在這個(gè)狀態(tài)停留,系統(tǒng)會(huì)接著回調(diào)onStart()
方法由Created
狀態(tài)進(jìn)入到Started
狀態(tài)怀读。 - 注意到诉位,
onCreate(xxxx)
是所有這些回調(diào)方法中唯一有參的,該參數(shù)保存了上次由于Activity
被動(dòng)回收時(shí)所保存的數(shù)據(jù)菜枷。
3.2 protected void onStart()
-
onStart()
方法執(zhí)行完后苍糠,Activity
就進(jìn)入了Started
狀態(tài),它也同樣不會(huì)在該狀態(tài)停留啤誊,而是接著回調(diào)onResume()
方法進(jìn)入Resumed
狀態(tài)岳瞭。 -
onStart()
被回調(diào)的情況有兩種: - 從
Created
狀態(tài)過來 - 從
Stopped
狀態(tài)過來,從這種狀態(tài)過來還會(huì)先經(jīng)過onRestart()
方法蚊锹。 - 由于它也會(huì)從
Stopped
狀態(tài)跳轉(zhuǎn)過來瞳筏,因此如果我們?cè)?code>onStop()當(dāng)中反注冊(cè)了一些廣播,或者釋放了一些資源牡昆,那么在這里需要重新注冊(cè)或者初始化姚炕,可以認(rèn)為,onStart()
和onStop()
是成對(duì)的關(guān)系丢烘。 -
Created
和Started
都不是持久性的狀態(tài)柱宦,那么為什么要提供一個(gè)onStart()
回調(diào)給開發(fā)者呢,直接由Created
狀態(tài)或者是Stopped
狀態(tài)經(jīng)過onResume()
這條路走到Resumed
狀態(tài)不就可以嗎播瞳,那么我們就要分析一下從onCreate()
到onStart()
掸刊,再到onResume()
的過程中,做了哪些其它的操作赢乓,這有利于我們?cè)谄綍r(shí)的開發(fā)中區(qū)分這兩個(gè)回調(diào)的使用場(chǎng)景忧侧,我們來看一下源碼。
首先我們看一下onStart()
方法調(diào)用的地方骏全,通過下面這段代碼苍柏,我們可以知道Activity
的onStart()
最初是通過Activity#performStart()
方法調(diào)用過來的:
<!-- Activity.java -->
private Instrumentaion mInstrumentation;
final void performStart() {
mInstrumentation.callActivityOnStart(this);
}
<!-- Instrumentaion.java -->
public void callActivityOnStart(Activity activity) {
activity.onStart();
}
而Activity#performStart()
方法是由ActivityThread#performLaunchActivity(
調(diào)過來的:
<!-- ActivityThread.java -->
private final void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity a = performLaunchActivity(r, customIntent); //performCreate, performStart()
if (a != null) {
....
handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed); //performResume()
....
}
}
//首先看一下調(diào)用performCreate, performStart的地方。
private final Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
mInstrumentation.callActivityOnCreate(activity, r.state); //performCreate
....
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state); //1.onRestoreIntanceState()
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
mInstrumentation.callActivityOnPostCreate(activity, r.state); //2.onPostCreate
if (!activity.mCalled) {
throw new SuperNotCalledException( "Activity " + r.intent.getComponent().toShortString() + " did not call through to super.onPostCreate()");
}
}
...
}
//這是performResume的入口姜贡。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {
ActivityClientRecord r = performResumeActivity(token, clearHide);
}
//最后看一下performResume真正執(zhí)行的地方试吁。
public final ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide) {
try {
if (r.pendingIntents != null) {
deliverNewIntents(r, r.pendingIntents); //3.onNewIntent()
r.pendingIntents = null;
}
if (r.pendingResults != null) {
deliverResults(r, r.pendingResults); //4.onActivityResult()
r.pendingResults = null;
}
r.activity.performResume();
}
- 通過上面這段代碼,我們可以得出以下幾點(diǎn)啟發(fā):
-
onStart()
到onResume()
的過程中,還可能會(huì)回調(diào)onRestoreInstanceState/onPostCreate/onNewIntent/onActvitiyResult
這幾個(gè)方法熄捍。 - 如果應(yīng)用退到后臺(tái)烛恤,再次被啟動(dòng)(
onNewIntent
),或者通過startActivityForResult
方法啟動(dòng)另一個(gè)Activity
得到結(jié)果返回(onActivityResult
)的時(shí)候余耽,在onStart()
方法當(dāng)中得到的并不是這兩個(gè)回調(diào)的最新結(jié)果缚柏,因?yàn)樯厦娴倪@兩個(gè)方法并沒有調(diào)用,而在onResume()
當(dāng)中碟贾,這點(diǎn)是可以得到保證的币喧。
3.3 protected void onResume()
- 該方法被回調(diào)時(shí),意味著
Activity
已經(jīng)完全可見了袱耽,但這個(gè)完全可見的概念并不等同于Activity
所屬的Window
被Attach
了杀餐,因?yàn)樵?code>Activity初次啟動(dòng)的時(shí)候,Attach
的操作是在回調(diào)onResume()
之后的朱巨,也就是下面的這段代碼
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) {
....
ActivityClientRecord r = performResumeActivity(token, clearHide);
....
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
WindowManager.LayoutParams l = r.window.getAttributes();
...
wm.addView(decor, l);
}
}
- 在
onResume()
方法被回調(diào)時(shí)史翘,由于DecorView
并不一定Attach
了,因此這時(shí)候我們獲取布局內(nèi)某些View
的寬高得到的值有可能是不正確的冀续,既然onResume()
當(dāng)中不能保證琼讽,那么onStart()
方法也是同理,所有需要在attachToWindow
之后才能執(zhí)行或是期望得到正確結(jié)果的操作都需要注意這一點(diǎn)洪唐。 - 在
onResume
當(dāng)中钻蹬,我們一般會(huì)做這么一些事:在可見時(shí)重新拉取一些需要及時(shí)刷新的數(shù)據(jù)、注冊(cè)ContentProvider
的監(jiān)聽桐罕,最重要的是在onPause()
中的一些釋放操作要在這里面恢復(fù)回來脉让。
3.4 protected void onPause()
- 該方法被回調(diào)時(shí),意味著
Activity
部分不可見功炮,例如一個(gè)半透明的界面覆蓋在了上面溅潜,這時(shí)候只要Activity
仍然有一部分可見,那么它會(huì)一直保持在Paused
狀態(tài)薪伏。 - 如果用戶從
Paused
狀態(tài)回到Resumed
狀態(tài)滚澜,只會(huì)回調(diào)onResume
方法。 - 在
onPause()
方法中嫁怀,應(yīng)該暫停正在進(jìn)行的頁面操作设捐,例如正在播放的視頻,或者釋放相機(jī)這些多媒體資源塘淑。 - 在
onPause()
當(dāng)中萝招,可以保存一些必要數(shù)據(jù)到持久化的存儲(chǔ),例如正在編寫的信息草稿存捺。 - 不應(yīng)該在這里執(zhí)行耗時(shí)的操作槐沼,因?yàn)樾陆缑鎲?dòng)的時(shí)候會(huì)先回調(diào)當(dāng)前頁面的
onPause()
方法曙蒸,所以如果進(jìn)行了耗時(shí)的操作,那么會(huì)影響到新界面的啟動(dòng)時(shí)間岗钩,官方文檔的建議是這些操作應(yīng)該放到onStop()
當(dāng)中去做纽窟,其實(shí)在onStop()
中也不應(yīng)當(dāng)做耗時(shí)的操作,因?yàn)樗彩窃谥骶€程當(dāng)中的兼吓,而在主線程中永遠(yuǎn)不應(yīng)該進(jìn)行耗時(shí)的操作臂港。 - 釋放系統(tǒng)資源,例如先前注冊(cè)的廣播视搏、使用的傳感器(如
GPS
)审孽、以及一些僅當(dāng)頁面獲得焦點(diǎn)時(shí)才需要的資源。 - 當(dāng)處于
Paused
狀態(tài)時(shí)浑娜,Activity
的實(shí)例是被保存在內(nèi)存中的瓷胧,因此在其重新回到Resumed
狀態(tài)的過程中,不需要重新初始化任何的組件棚愤。
3.5 protected void onStop()
- 該方法被回調(diào)時(shí),表明
Activity
已經(jīng)完全不可見了杂数。 - 在任何場(chǎng)景下宛畦,系統(tǒng)都會(huì)先回調(diào)
onPause()
,之后再回調(diào)onStop()
揍移。 - 官方文檔有談到次和,當(dāng)
onStop()
執(zhí)行完之后,系統(tǒng)有可能會(huì)銷毀這個(gè)Activity
實(shí)例那伐,在某些極端情況下踏施,有可能不會(huì)回調(diào)onDestroy()
方法,因此罕邀,我們需要在onStop()
當(dāng)中釋放掉一些資源來避免內(nèi)存泄漏畅形,而onDestory()
中需要釋放的是和Activity
相關(guān)的資源,如線程之類的(這點(diǎn)平時(shí)在工作中很少用诉探,一般我們都是在onDestroy()
中釋放所有資源日熬,而且也沒有去實(shí)現(xiàn)onStart()
方法,究竟什么時(shí)候不會(huì)走onDestroy()
肾胯,這點(diǎn)值得研究竖席,目前的猜測(cè)是該Activity
在別的地方被引用了,導(dǎo)致其不能回收)敬肚。 - 當(dāng)
Activity
從Stopped
狀態(tài)回到前臺(tái)時(shí)毕荐,會(huì)先回調(diào)onRestart()
方法,然而我們更多的是使用onStart()
方法作為onStop()
的對(duì)應(yīng)關(guān)系艳馒,因?yàn)闊o論是從Stopped
狀態(tài)憎亚,還是從Created
狀態(tài)跳轉(zhuǎn)到Resumed
狀態(tài),都是需要初始化必要的資源的,而它們經(jīng)過的共同路徑是onStart()
虽填。
3.6 protected void onDestroy()
- 該方法被回調(diào)時(shí)丁恭,表明
Activity
是真正要被銷毀了, - 此時(shí)應(yīng)當(dāng)釋放掉所有用到的資源斋日,避免內(nèi)存泄漏牲览。