Activity 知識梳理(1) - Activity生命周期

一、概述

學習Activity生命周期,首先我們要明白,學習它的目的不僅在于要知道有哪些生命周期,而是在于明白各回掉函數(shù)調用的時機,以便在合適的時機進行正確的操作,如初始化變量、頁面展示、數(shù)據(jù)操作、資源回收等。平時的工作習慣都是,onCreate(xxx)初始化,onResume()注冊、拉取數(shù)據(jù),onPause()反注冊,onDestroy()釋放資源,這篇文章總結了一些和關鍵生命周期相關聯(lián)的一些要點。

二、金字塔模型

Activity 生命周期金字塔模型.png

在官方文檔中,把Activity的生命周期理解成為一個金字塔模型,它是基于下面兩點:

  • 金字塔的每個階梯表示Activity所處的狀態(tài)
  • 回調函數(shù)則是各狀態(tài)轉換過程當中所經(jīng)過的路徑

這個模型中包含了Activity的六種狀態(tài):

  • Created:創(chuàng)建完成
  • Started:可見
  • Resumed:可見
  • Paused:部分可見
  • Stopped:不可見
  • Destroyed:銷毀

在這六種狀態(tài)當中,只有Resumed、PausedStopped這幾種狀態(tài)在用戶沒有進一步操作時會保持在該狀態(tài),而其余的,都會在執(zhí)行完相應的回調函數(shù)后快速跳過。

三、關鍵生命周期

3.1 protected void onCreate(Bundle savedInstanceState)

  • 該方法被回調時,意味著一個新的Activity被創(chuàng)建了。
  • 由于當前處于一個新的Activity實體對象當中,所有的東西都是未初始化的,我們一般需要做的事情包括調用setContentView方法設置該Activity的布局,初始化類成員變量。
  • onCreate(xxx)方法執(zhí)行完之后,Activity就進入了Created狀態(tài),然而它并不會在這個狀態(tài)停留,系統(tǒng)會接著回調onStart() 方法由Created狀態(tài)進入到Started狀態(tài)。
  • 注意到,onCreate(xxxx)是所有這些回調方法中唯一有參的,該參數(shù)保存了上次由于Activity被動回收時所保存的數(shù)據(jù)。

3.2 protected void onStart()

  • onStart()方法執(zhí)行完后,Activity就進入了Started狀態(tài),它也同樣不會在該狀態(tài)停留,而是接著回調 onResume()方法進入Resumed狀態(tài)。
  • onStart()被回調的情況有兩種:
  • Created狀態(tài)過來
  • Stopped狀態(tài)過來,從這種狀態(tài)過來還會先經(jīng)過onRestart()方法。
  • 由于它也會從Stopped狀態(tài)跳轉過來,因此如果我們在onStop()當中反注冊了一些廣播,或者釋放了一些資源,那么在這里需要重新注冊或者初始化,可以認為,onStart()onStop()是成對的關系。
  • CreatedStarted都不是持久性的狀態(tài),那么為什么要提供一個onStart()回調給開發(fā)者呢,直接由Created狀態(tài)或者是 Stopped狀態(tài)經(jīng)過onResume()這條路走到Resumed狀態(tài)不就可以嗎,那么我們就要分析一下從 onCreate()onStart(),再到onResume()的過程中,做了哪些其它的操作,這有利于我們在平時的開發(fā)中區(qū)分這兩個回調的使用場景,我們來看一下源碼。

首先我們看一下onStart()方法調用的地方,通過下面這段代碼,我們可以知道ActivityonStart()最初是通過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真正執(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(); 
}
  • 通過上面這段代碼,我們可以得出以下幾點啟發(fā):
  • onStart()onResume()的過程中,還可能會回調onRestoreInstanceState/onPostCreate/onNewIntent/onActvitiyResult這幾個方法。
  • 如果應用退到后臺,再次被啟動(onNewIntent),或者通過startActivityForResult方法啟動另一個 Activity得到結果返回(onActivityResult)的時候,在onStart()方法當中得到的并不是這兩個回調的最新結果,因為上面的這兩個方法并沒有調用,而在onResume()當中,這點是可以得到保證的。

3.3 protected void onResume()

  • 該方法被回調時,意味著Activity已經(jīng)完全可見了,但這個完全可見的概念并不等同于Activity所屬的WindowAttach了,因為在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之后才能執(zhí)行或是期望得到正確結果的操作都需要注意這一點。
  • onResume當中,我們一般會做這么一些事:在可見時重新拉取一些需要及時刷新的數(shù)據(jù)、注冊 ContentProvider的監(jiān)聽,最重要的是在onPause()中的一些釋放操作要在這里面恢復回來。

3.4 protected void onPause()

  • 該方法被回調時,意味著Activity部分不可見,例如一個半透明的界面覆蓋在了上面,這時候只要Activity仍然有一部分可見,那么它會一直保持在Paused狀態(tài)。
  • 如果用戶從Paused狀態(tài)回到Resumed狀態(tài),只會回調onResume方法。
  • onPause()方法中,應該暫停正在進行的頁面操作,例如正在播放的視頻,或者釋放相機這些多媒體資源。
  • onPause()當中,可以保存一些必要數(shù)據(jù)到持久化的存儲,例如正在編寫的信息草稿。
  • 不應該在這里執(zhí)行耗時的操作,因為新界面啟動的時候會先回調當前頁面的onPause()方法,所以如果進行了耗時的操作,那么會影響到新界面的啟動時間,官方文檔的建議是這些操作應該放到 onStop()當中去做,其實在onStop()中也不應當做耗時的操作,因為它也是在主線程當中的,而在主線程中永遠不應該進行耗時的操作。
  • 釋放系統(tǒng)資源,例如先前注冊的廣播、使用的傳感器(如GPS)、以及一些僅當頁面獲得焦點時才需要的資源。
  • 當處于Paused狀態(tài)時,Activity的實例是被保存在內存中的,因此在其重新回到Resumed狀態(tài)的過程中,不需要重新初始化任何的組件。

3.5 protected void onStop()

  • 該方法被回調時,表明Activity已經(jīng)完全不可見了。
  • 在任何場景下,系統(tǒng)都會先回調onPause(),之后再回調onStop()。
  • 官方文檔有談到,當onStop()執(zhí)行完之后,系統(tǒng)有可能會銷毀這個Activity實例,在某些極端情況下,有可能不會回調onDestroy()方法,因此,我們需要在onStop()當中釋放掉一些資源來避免內存泄漏,而 onDestory()中需要釋放的是和Activity相關的資源,如線程之類的(這點平時在工作中很少用,一般我們都是在onDestroy()中釋放所有資源,而且也沒有去實現(xiàn)onStart() 方法,究竟什么時候不會走onDestroy(),這點值得研究,目前的猜測是該Activity在別的地方被引用了,導致其不能回收)。
  • ActivityStopped狀態(tài)回到前臺時,會先回調onRestart()方法,然而我們更多的是使用onStart() 方法作為onStop()的對應關系,因為無論是從Stopped狀態(tài),還是從Created狀態(tài)跳轉到Resumed狀態(tài),都是需要初始化必要的資源的,而它們經(jīng)過的共同路徑是onStart()。

3.6 protected void onDestroy()

  • 該方法被回調時,表明Activity是真正要被銷毀了,
  • 此時應當釋放掉所有用到的資源,避免內存泄漏。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容