深入學習Activity生命週期

BaiHongHua發表於2017-10-04

前言

在 Android 四大元件中,Activity 在其中佔有的位置也是非常重要的; 吃透 Activity 的生命週期的流程,無疑對在開發 Android 是非常有幫助的,那麼,今天讓我們一起來深入學習 Android 的生命週期吧!

在學習 Actvity 的生命週期之前,首先聊聊筆者本人對 Activity 的瞭解吧。在筆者剛剛接觸 Android 開發的時候,對 Activity 是什麼還是挺疑惑的,而且挺多入門的課本也沒有詳細去介紹到底什麼是 Activity,大多簡單一句 Activity 是 Android 的四大元件之一,然後就沒有它什麼事了,就開始介紹它的生命週期的回撥函式...
可能現在看這篇文章的你可能會想,Activity 不就是一個活動,我們平常在 Android 手機的一整螢幕裡面看到的全部內容嗎?例如下圖手機螢幕嗎?

筆者認為這是對 Activity 的認識有偏頗的,在手機一個螢幕裡面看到的那不是一個 Activity,是一個繼承於 FrameLayout 的 DecorView 下面的子類 View,而 Activity 只是作為一個載體去承載這些檢視 View。筆者認為可以把 Activity 看作一個載體,而不是在手機螢幕上面看到的檢視吧。

Activity 的層級結構


因為我拿這張圖出來主要是為了更好地說明 Activity 可以看作為一個載體,而不是在手機螢幕看到的檢視(TitleView 和 ContentView),所以更往具體的原始碼實現,因為筆者的能力暫時有限,在這裡就暫時不深究了:);

Activity 生命週期流程圖

activity_lifecycle
activity_lifecycle

Activity 在 Android 系統裡面都是被 activity stack (activity 棧)管理著(棧管理元素的特點是先進後出),在當一個新的 Activity 被啟動的時候,這個新的 Activity 就會被壓進 activity stack 的裡面,成為 activity stack 的棧頂的元素,並且這個 Activity 開始準備與使用者進行互動,也就是成為活動的 Activity(需要注意的一點,當前與使用者進行互動的 Activity 會一直保留著棧頂的位置,直到當前的與使用者互動的 Activity 被退出,其後面的 Activity 會再次回到棧頂而成為活動的 Activity)。

新的 Activity 被壓進任務棧,或者當前與使用者進行互動的 Activity 被彈出任務棧,在壓棧或者彈出棧的過程中,都會觸發上面 Activity 生命週期流程圖中的對應的回撥函式。

1.0 Activity中每個回撥函式所代表的狀態

  • onCreate() 在 activity 第一次被建立的時候被回撥,在這個回撥函式裡面,主要是用來設定要顯示的佈局檢視、以及把資料繫結到佈局檢視上面去,還有初始化變數等操作;

    • onRestrart() 在 Activity 完全不可見,但 Activity 沒有被銷燬的情況下,在這個 Activity 重新回到棧頂的時候(當往全不可見的時候,這個 Activity 不在棧頂)被回撥,緊接著會回撥 onStart();

    • onStart() 在 Activity 對於使用者來說全部可見,但是還沒有獲取到焦點,也就是使用者還不可以在上面進行操作的時候被回撥,但是這個回撥的過程對於使用者來說,是非常短暫的,使用者察覺不出來;

      • onResume() 在當前的 Activity 獲取到焦點的前被回撥,也就是回撥後,使用者可以在上面進行操作,例如可以點選輸入框進行輸入等一系列的操作,這時候對於使用者,內容是全部可見的;

      • onPause() 在但前內容對於使用者只是部分可見的時候被回撥(注意:介面彈出一個 dialog、或者點選輸入框而彈出輸入框是不會回撥該函式,雖然這時候也是相對使用者部分可見),例如有個對話方塊風格的 Activity 彈出的時候,會回撥該函式;

    • onStop 當在 Activity 的檢視完全不可見的時候,這個函式會被回撥;

  • onDestroy() 在這個 Activity 退出執行 finish() 方法,或者在系統記憶體緊張的時候會殺死這個 Activity 而回撥這一個函式;

上面的回撥函式,在使用者開啟一個 Activity、關閉一個 Activity、重新開啟一個 Activity 的時候對應的回撥函式。

2.0 通過 Dubug 模式檢視 Activity 的生命週期

下面的程式碼是筆者進行 Dubug 的時候使用的原始碼:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "測試Activity的生命週期";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.e(TAG, "onCreate: ");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart: ");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.e(TAG, "onResume: ");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause: ");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop: ");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.d(TAG, "onRestart: ");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }


}複製程式碼
2.1 首次把一個 Activity 新增到棧頂:

202017-10-04
202017-10-04

2.2 按下選單鍵的時候 Activity 的狀態:

202017-10-04
202017-10-04

2.3 當一個對話式的 Activity 執行在任務棧頂的時候:

2017-08
2017-08

這個 Dubug 模式主要是為了檢驗,Activity 在彈出輸入框、Fragment 型別的 Dialog、非 Fragment 型別的 Dialog 的時候,該 Activity 的生命週期的變化,從控制檯的日誌可以看出,上面三種情況都不會導致 Activity 的生命週期發生變化,而當筆者點選第三個按鈕的時候(這個按鈕監控的是個只是展示圖片、dialog 形式的 Activity),控制檯列印出了 onPause() 回撥函式,也就是在 Activity 部分可見的時候,或者這樣子說:當一個體積大 Activity 被 另一個體積小的 Activity 遮住的時候,體積大的 Activity 對於使用者仍然部分可見,這時候會回撥 onPause() 函式;
當體積小的 Activity 被隱藏起來,這時候體積大的 Activity 就會回到棧頂,這時候回到棧頂前,就會回撥 onResume() 函式,再次準備與使用者進行互動;

2.4 關閉 Activity,即把該 Activity 從棧頂移除:

2000-39
2000-39

3.0 橫豎屏切換時 Activity 的生命週期的變化

3.1 由豎屏切換到橫屏時,Activity 的生命週期的變化

2000-47
2000-47


3.2 由橫屏屏切換到豎屏時,Activity 的生命週期的變化

2000-48
2000-48


由上面橫豎屏切換除錯的日誌可以看出,當橫豎屏進行切換的時候,Activity 的會回撥 onDestroy() 函式,然後再次回撥 onCreat() 函式,但是,有時候我們不允許應用在切換橫豎屏的時候,發生回撥 onDestroy() 函式,然後在回撥 onCreat() 函式。這時候,如果不想讓 Activity 重新呼叫 onCreat() 函式,可以有下面兩個解決方法:

  • 在 AndroidManifest.xml裡面配置相關屬性,直接不允許進行橫豎屏的切換;
    主要是在自己不允許進行橫豎屏切換的 Activity 配置 android:screenOrientation="portrait"這個屬性,portrait 是指只能豎屏,不可以橫屏;而 landscape 則反之。

application 節點下的程式碼如下:(如下面註釋 1 )

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:screenOrientation="portrait"> <!--1-->
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>複製程式碼
  • 在 AndroidManifest.xml裡面配置相關屬性,直接宣告該 Activity 不發生變化
    主要是在自己不允許進行橫豎屏切換的 Activity 配置 android:configChanges="orientation|keyboardHidden|screenSize" 這個屬性。

application 節點下的程式碼如下:(如下面註釋 2 )

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:configChanges="orientation|keyboardHidden|screenSize"> <!--2-->
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>複製程式碼

小結

這次對於 Activity 的生命週期的流程分析,只是在其 API 介面的層面上面的分析。對於 Activity 的生命週期的分析,筆者認為,只要抓住 Activity 的變化、Activity 與 Activity 之間切換的關係以及他們在 Activity 棧中的位置,就會很好地分析出 Activity 的生命週期的變化的。

相關文章