Activity 知識梳理(1) Activity生命週期

澤毛發表於2017-12-21

一、概述

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

二、金字塔模型

Activity 生命週期金字塔模型.png
在官方文件中,把Activity的生命週期理解成為一個金字塔模型,它是基於下面兩點:

  • 金字塔的每個階梯表示**Activity所處的狀態**
  • 回撥函式則是各狀態轉換過程當中所經過的路徑

這個模型中包含了Activity的六種狀態:

  • Created:建立完成
  • Started:可見
  • Resumed:可見
  • Paused:部分可見
  • Stopped:不可見
  • Destroyed:銷燬

在這六種狀態當中,只有ResumedPausedStopped這幾種狀態在使用者沒有進一步操作時會保持在該狀態,而其餘的,都會在執行完相應的回撥函式後快速跳過。

三、關鍵生命週期

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()是成對的關係。
  • CreatedStarted都不是永續性的狀態,那麼為什麼要提供一個onStart()回撥給開發者呢,直接由Created狀態或者是 Stopped狀態經過onResume()這條路走到Resumed狀態不就可以嗎,那麼我們就要分析一下從 onCreate()onStart(),再到onResume()的過程中,做了哪些其它的操作,這有利於我們在平時的開發中區分這兩個回撥的使用場景,我們來看一下原始碼。

首先我們看一下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真正執行的地方。
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所屬的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之後才能執行或是期望得到正確結果的操作都需要注意這一點。
  • 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在別的地方被引用了,導致其不能回收)。
  • ActivityStopped狀態回到前臺時,會先回撥onRestart()方法,然而我們更多的是使用onStart() 方法作為onStop()的對應關係,因為無論是從Stopped狀態,還是從Created狀態跳轉到Resumed狀態,都是需要初始化必要的資源的,而它們經過的共同路徑是onStart()

3.6 protected void onDestroy()

  • 該方法被回撥時,表明Activity是真正要被銷燬了,
  • 此時應當釋放掉所有用到的資源,避免記憶體洩漏。

相關文章