Android開發藝術探索知識回顧——第1章 Activity的生命週期和啓動模式

 

第1章 Activity的生命週期和啓動模式

作爲本書的第1章,本章主要介紹Activity相關的一些內容。Activity作爲四大組件之 首,是使用最爲頻繁的一種組件,中文直接翻譯爲“活動",但是筆者認爲這種翻譯有些生硬,如果翻譯成界面就會更好理解。正常情況下,除了 WindowDialogToast,我們能 見到的界面的確只有Activity。Activity是如此重要,以至於本書開篇就不得不講到它。

當然,由於本書的定位爲進階書,所以不會介紹如何啓動Activity這類入門知識,本章的側重點是Activity在使用過程中的一些不容易搞清楚的概念,主要包括生命週期和啓動模式以及IntentFilter的匹配規則分析。其中Activity在異常情況下的生命週期是十分微妙的, 至於Activity的啓動模式和形形色色的Flags更是讓初學者摸不到頭腦,就連隱式啓動 Activity中也有着複雜的Intent匹配過程,不過不用擔心,本章接下來將一一解開這些疑難 問題的神祕面紗。

 

1.1 Activity的生命週期全面分析

本節將Activity的生命週期分爲兩部分內容,一部分是典型情況下的生命週期,另一部分是異常情況下的生命週期。

所謂典型情況下的生命週期,是指在有用戶參與的情況下,Activity所經過的生命週期的改變;

而異常情況下的生命週期,是指Activity被系統回收 或者 由於當前設備的Configuration發生改變從而導致Activity被銷燬重建,異常情況下的生命週期的關注點和典型情況下略有不同。

 

1.1.1典型情況下的生命週期分析

在正常情況下,Activity會經歷如下生命週期。

  1. onCreate表示Activity正在被創建,這是生命週期的第一個方法。在這個方法中, 我們可以做一些初始化工作,比如調用setContentView去加載界面佈局資源、初始化Activity 所需數據等。
  2. onRestart表示Activity正在重新啓動。一般情況下,噹噹前Activity從不可見重新變爲可見狀態時,onRestart 就會被調用。這種情形一般是用戶行爲所導致的,比如用戶按Home鍵切換到桌面或者用戶打開了一個新的 Activity,這時當前的 Activity 就會暫停,也就是 onPause 和 onStop 被執行了,接着用戶又回到了這個 Activity,就會出現這種情況。
  3. onStart表示Activity正在被啓動,即將開始,這時 Activity 已經可見了,但是還沒有出現在前臺,還無法和用戶交互。這個時候其實可以理解爲Activity已經顯示出來了,但是我們還看不到。
  4. onResume表示Activity已經可見了,並且出現在前臺並開始活動。要注意這個和 onStart 的對比,onStart 和 onResume 都表示Activity已經可見,但是 onStart 的時候 Activity 還在後臺,onResume 的時候 Activity 才顯示到前臺。
  5. onPause表示 Activity 正在停止,正常情況下,緊接着 onStop 就會被調用。在特殊情況下,如果這個時候快速地再回到當前Activity,那麼onResume會被調用。筆者的理解是,這種情況屬於極端情況,用戶操作很難重現這一場景。此時可以做一些存儲數據、 停止動畫等工作,但是注意不能太耗時,因爲這會影響到新Activity的顯示,onPause必須先執行完,新ActivityonResume纔會執行。
  6. onStop表示Activity即將停止,可以做一些稍微重量級的回收工作,同樣不能太耗時。
  7. onDestroy表示Activity即將被銷燬,這是Activity生命週期中的最後一個回調, 在這裏,我們可以做一些回收工作和最終的資源釋放。

正常情況下,Activity的常用生命週期就只有上面7個,圖1-1更詳細地描述了 Activity 各種生命週期的切換過程。

針對圖1-1,這裏再附加一下具體說明,分如下幾種情況。

(1)針對一個特定的 Activity,第一次啓動,回調如下:onCreate -> onStart -> onResume

(2)當用戶打開新的Activity或者切換到桌面的時候,回調如下:onPause -> onStop 。這裏有一種特殊情況,如果新Activity釆用了透明主題,那麼當前Activity不會回調onStop。

(3)當用戶再次回到原 Activity 時,回調如下:onRestart-> onStart -> onResume。

(4)當用戶按back鍵回退時,回調如下:onPause -> onStop -> onDestroy

(5)當Activity被系統回收後再次打開,生命週期方法回調過程和(1)一樣,注意只是生命週期方法一樣,不代表所有過程都一樣,這個問題在下一節會詳細說明。

(6)從整個生命週期來說,onCreate 和 onDestroy 是配對的,分別標識着 Activity 的創建和銷燬,並且只可能有一次調用。從 Activity 是否可見來說,onStart 和 onStop 是配對的,隨着用戶的操作或者設備屏幕的點亮和熄滅,這兩個方法可能被調用多次;從 Activity 是否在前臺來說 onResume 和 onPause 是配對的,隨着用戶操作或者設備屏幕的點亮和熄滅, 這兩個方法可能被調用多次。

這裏提出2個問題,不知道大家是否清楚。

問題1:onStartonResumeonPauseonStop從描述上來看差不多,對我們來說有什麼實質的不同呢?

問題2:假設當前 Activity A,如果這時用戶打開一個新 Activity B,那麼 的 onResume 和 的 onPause 哪個先執行呢?

先說第一個問題,從實際使用過程來說,onStart 和 onResumeonPause 和 onStop 看起來的確差不多,甚至我們可以只保留其中一對,比如只保留 onStart 和 onStop 既然如此, 那爲什麼Android系統還要提供看起來重複的接口呢?根據上面的分析,我們知道,這兩個配對的回調分別表示不同的意義,onStart 和 onStop 是從 Activity 是否可見這個角度來回調的,而 onResume 和 onPause 是從Activity是否位於前臺這個角度來回調的,除了這種區別,在實際使用中沒有其他明顯區別。

第二個問題可以從 Android 源碼裏得到解釋。關於 Activity 的工作原理在本書後續章節會進行介紹,這裏我們先大概瞭解即可。從Activity的啓動過程來看,我們來看一下系統源碼。Activity的啓動過程的源碼相當複雜,涉及Instrumentation、ActivityThread和ActivityManagerService(下面簡稱AMS)這裏不詳細分析這一過程,簡單理解,啓動 Activity 的請求會由 Instrumentation 來處理,然後它通過 Binder 向 AMS 發請求,AMS內部維護着一個 ActivityStack 並負責棧內的 Activity 的狀態同步,AMS 通過 ActivityThread 去同步 Activity 的狀態從而完成生命週期方法的調用。在 ActivityStack 中的 resumeTopActivity-InnerLocked 方法中,有這麼一段代碼:

從上述代碼可以看出,在新 Activity 啓動之前,棧頂的 Activity 需要先 onPause 後,新 Activity 才能啓動。

最終,在 ActivityStackSupervisor 中的 realStartActivityLocked 方法會調用如下代碼。

我們知道,這個 app.thread 的類型是 IApplicationThread,而 IApplicationThread 的具體實現是 ActivityThread 中的 ApplicationThread 。所以,這段代碼實際上調到了 ActivityThread 的中,即 ApplicationThread scheduleLaunchActivity 方法,而 scheduleLaunchActivity 方法最終會完成新 Activity 的 onCreateonStart、onResume 的調用過程。因此,可以得出結論, 是舊 Activity 先 onPause,然後新Activity 再啓動。

至於 ApplicationThread scheduleLaunchActivity 方法爲什麼會完成新 Activity onCreate、onStart、onResume 的調用過程,請看下面的代碼。scheduleLaunchActivity 最終會調用如下方法,而如下方法的確會完成 onCreate、onStart、onResume 的調用過程。

從上面的分析可以看出,當新啓動一個 Activity 的時候,舊 Activity 的 onPause 會先執行,然後纔會啓動新的 Activity 到底是不是這樣呢? 我們寫個例子驗證一下,如下是2個 Activity 的代碼,在 MainActivity 中單擊按鈕可以跳轉到 Second Activity,同時爲了分析我們的問題,在生命週期方法中打印出了日誌,通過日誌我們就能看出它們的調用順序。

代碼:MainActivity.java

package com.yyh.demo1.activitysample;

import com.ryg.chapter_1.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;


public class MainActivity extends Activity {

    public static final String TAG = "MainActivity@@@";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_demo1);

        findViewById(R.id.btnTo).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });

    }

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

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

代碼:SecondActivity.java

package com.yyh.demo1.activitysample;

import com.ryg.chapter_1.R;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;


public class SecondActivity extends Activity {

    private static final String TAG = "SecondActivity@@@";

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

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

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

我們來看一下log,是不是和我們上面分析的一樣,如圖1-2所示。

通過圖1-2可以發現,舊 Activity 的 onPause 先調用,然後新 Activity 才啓動,這也證實了我們上面的分析過程。也許有人會問,你只是分析了 Android5.0 的源碼,你怎麼知道所有版木的源碼都是相同邏輯呢?關於這個問題,我們的確不大可能把所有版本的源碼都分析一遍,但是作爲Android運行過程的基本機制,隨着版本的更新並不會有大的調整, 因爲Android系統也需要兼容性,不能說在不同版本上同一個運行機制有着截然不同的表現。關於這一點我們需要把握一個度,就是對於Android運行的基本機制在不同 Android 版本上具有延續性。從另一個角度來說,Android官方文檔對 onPause 的解釋有這麼一句:不能在 onPause 中做重量級的操作,因爲必須 onPause 執行完成以後新 Activity 才能 Resume從這一點也能間接證明我們的結論。通過分析這個問題,我們知道 onPause 和 onStop 都不能執行耗時的操作,尤其是 onPause方法,這也意味着,我們應當儘量在 onStop 中做操作,從而使得新 Activity 儘快顯示出來並切換到前臺。

 

1.1.2異常情況下的生命週期分析

上一節我們分析了典型情況下 Activity 的生命週期,本節我們接着分析 Activity 在異常情況下的生命週期。我們知道,Activity除了受用戶操作所導致的正常的生命週期方法調度, 還有一些異常情況,比如當資源相關的系統配置發生改變以及系統內存不足時,Activity就可能被殺死。下面我們具體分析這兩種情況。

1.情況1:資源相關的系統配置發生改變導致Activity被殺死並重新創建

理解這個問題,我們首先要對系統的資源加載機制有一定了解,這裏不詳細分析系統 的資源加載機制,只是簡單說明一下。拿最簡單的圖片來說,當我們把一張圖片放在 drawable目錄後,就可以通過Resources去獲取這張圖片。同時爲了兼容不同的設備,我們 可能還需要在其他一些目錄放置不同的圖片,比如drawable-mdpidrawable-hdpidrawable-land等。這樣,當應用程序啓動時,系統就會根據當前設備的情況去加載合適的

 

 

 

                                       

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章