一、概述
學習Activity
生命週期,首先我們要明白,學習它的目的不僅在於要知道有哪些生命週期,而是在於明白各回掉函式呼叫的時機,以便在合適的時機進行正確的操作,如初始化變數、頁面展示、資料操作、資源回收等。平時的工作習慣都是,onCreate(xxx)
初始化,onResume()
註冊、拉取資料,onPause()
反註冊,onDestroy()
釋放資源,這篇文章總結了一些和關鍵生命週期相關聯的一些要點。
二、金字塔模型
在官方文件中,把Activity
的生命週期理解成為一個金字塔模型,它是基於下面兩點:
- 金字塔的每個階梯表示**
Activity
所處的狀態** - 各回撥函式則是各狀態轉換過程當中所經過的路徑
這個模型中包含了Activity
的六種狀態:
Created
:建立完成Started
:可見Resumed
:可見Paused
:部分可見Stopped
:不可見Destroyed
:銷燬
在這六種狀態當中,只有Resumed
、Paused
、Stopped
這幾種狀態在使用者沒有進一步操作時會保持在該狀態,而其餘的,都會在執行完相應的回撥函式後快速跳過。
三、關鍵生命週期
3.1 protected void onCreate(Bundle savedInstanceState)
- 該方法被回撥時,意味著一個新的
Activity
被建立了。 - 由於當前處於一個新的
Activity
實體物件當中,所有的東西都是未初始化的,我們一般需要做的事情包括呼叫setContentView
方法設定該Activity
的佈局,初始化類成員變數。 onCreate(xxx)
方法執行完之後,Activity
就進入了Created
狀態,然而它並不會在這個狀態停留,系統會接著回撥onStart()
方法由Created
狀態進入到Started
狀態。- 注意到,
onCreate(xxxx)
是所有這些回撥方法中唯一有參的,該引數儲存了上次由於Activity
被動回收時所儲存的資料。
3.2 protected void onStart()
onStart()
方法執行完後,Activity
就進入了Started
狀態,它也同樣不會在該狀態停留,而是接著回撥onResume()
方法進入Resumed
狀態。onStart()
被回撥的情況有兩種:- 從
Created
狀態過來 - 從
Stopped
狀態過來,從這種狀態過來還會先經過onRestart()
方法。 - 由於它也會從
Stopped
狀態跳轉過來,因此如果我們在onStop()
當中反註冊了一些廣播,或者釋放了一些資源,那麼在這裡需要重新註冊或者初始化,可以認為,onStart()
和onStop()
是成對的關係。 Created
和Started
都不是永續性的狀態,那麼為什麼要提供一個onStart()
回撥給開發者呢,直接由Created
狀態或者是Stopped
狀態經過onResume()
這條路走到Resumed
狀態不就可以嗎,那麼我們就要分析一下從onCreate()
到onStart()
,再到onResume()
的過程中,做了哪些其它的操作,這有利於我們在平時的開發中區分這兩個回撥的使用場景,我們來看一下原始碼。
首先我們看一下onStart()
方法呼叫的地方,通過下面這段程式碼,我們可以知道Activity
的onStart()
最初是通過Activity#performStart()
方法呼叫過來的:
<!-- Activity.java -->
private Instrumentaion mInstrumentation;
final void performStart() {
mInstrumentation.callActivityOnStart(this);
}
<!-- Instrumentaion.java -->
public void callActivityOnStart(Activity activity) {
activity.onStart();
}
複製程式碼
而Activity#performStart()
方法是由ActivityThread#performLaunchActivity(
調過來的:
<!-- 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()
....
}
}
//首先看一下呼叫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真正執行的地方。
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();
}
複製程式碼
- 通過上面這段程式碼,我們可以得出以下幾點啟發:
onStart()
到onResume()
的過程中,還可能會回撥onRestoreInstanceState/onPostCreate/onNewIntent/onActvitiyResult
這幾個方法。- 如果應用退到後臺,再次被啟動(
onNewIntent
),或者通過startActivityForResult
方法啟動另一個Activity
得到結果返回(onActivityResult
)的時候,在onStart()
方法當中得到的並不是這兩個回撥的最新結果,因為上面的這兩個方法並沒有呼叫,而在onResume()
當中,這點是可以得到保證的。
3.3 protected void onResume()
- 該方法被回撥時,意味著
Activity
已經完全可見了,但這個完全可見的概念並不等同於Activity
所屬的Window
被Attach
了,因為在Activity
初次啟動的時候,Attach
的操作是在回撥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()
方法被回撥時,由於DecorView
並不一定Attach
了,因此這時候我們獲取佈局內某些View
的寬高得到的值有可能是不正確的,既然onResume()
當中不能保證,那麼onStart()
方法也是同理,所有需要在attachToWindow
之後才能執行或是期望得到正確結果的操作都需要注意這一點。 - 在
onResume
當中,我們一般會做這麼一些事:在可見時重新拉取一些需要及時重新整理的資料、註冊ContentProvider
的監聽,最重要的是在onPause()
中的一些釋放操作要在這裡面恢復回來。
3.4 protected void onPause()
- 該方法被回撥時,意味著
Activity
部分不可見,例如一個半透明的介面覆蓋在了上面,這時候只要Activity
仍然有一部分可見,那麼它會一直保持在Paused
狀態。 - 如果使用者從
Paused
狀態回到Resumed
狀態,只會回撥onResume
方法。 - 在
onPause()
方法中,應該暫停正在進行的頁面操作,例如正在播放的視訊,或者釋放相機這些多媒體資源。 - 在
onPause()
當中,可以儲存一些必要資料到持久化的儲存,例如正在編寫的資訊草稿。 - 不應該在這裡執行耗時的操作,因為新介面啟動的時候會先回撥當前頁面的
onPause()
方法,所以如果進行了耗時的操作,那麼會影響到新介面的啟動時間,官方文件的建議是這些操作應該放到onStop()
當中去做,其實在onStop()
中也不應當做耗時的操作,因為它也是在主執行緒當中的,而在主執行緒中永遠不應該進行耗時的操作。 - 釋放系統資源,例如先前註冊的廣播、使用的感測器(如
GPS
)、以及一些僅當頁面獲得焦點時才需要的資源。 - 當處於
Paused
狀態時,Activity
的例項是被儲存在記憶體中的,因此在其重新回到Resumed
狀態的過程中,不需要重新初始化任何的元件。
3.5 protected void onStop()
- 該方法被回撥時,表明
Activity
已經完全不可見了。 - 在任何場景下,系統都會先回撥
onPause()
,之後再回撥onStop()
。 - 官方文件有談到,當
onStop()
執行完之後,系統有可能會銷燬這個Activity
例項,在某些極端情況下,有可能不會回撥onDestroy()
方法,因此,我們需要在onStop()
當中釋放掉一些資源來避免記憶體洩漏,而onDestory()
中需要釋放的是和Activity
相關的資源,如執行緒之類的(這點平時在工作中很少用,一般我們都是在onDestroy()
中釋放所有資源,而且也沒有去實現onStart()
方法,究竟什麼時候不會走onDestroy()
,這點值得研究,目前的猜測是該Activity
在別的地方被引用了,導致其不能回收)。 - 當
Activity
從Stopped
狀態回到前臺時,會先回撥onRestart()
方法,然而我們更多的是使用onStart()
方法作為onStop()
的對應關係,因為無論是從Stopped
狀態,還是從Created
狀態跳轉到Resumed
狀態,都是需要初始化必要的資源的,而它們經過的共同路徑是onStart()
。
3.6 protected void onDestroy()
- 該方法被回撥時,表明
Activity
是真正要被銷燬了, - 此時應當釋放掉所有用到的資源,避免記憶體洩漏。