天天看點

【Android重點知識突破系列1】——Activity

目錄

    • 一.Activity概念
    • 二.Activity生命周期
    • 三.啟動模式
      • 3.1 Activity四種啟動模式
        • 3.1.1 基本概念
        • 3.1.2 taskAffinity屬性
        • 3.1.3 Intent中标志位設定啟動模式
        • 3.1.4 startActivity場景
      • 3.2 Activity四種啟動模式的使用場景
      • 3.3 Intent
        • 3.3.1 Intent屬性
        • 3.3.2 Intent的顯式與隐式
      • 3.4 Activity的異常生命周期
        • 3.4.1 系統配置改變引起異常
        • 3.4.2 系統回收引起異常
        • 3.4.3 異常基礎處理
    • 四. onConfigurationChanged()和onNewIntent()
      • 4.1 onConfiguratonChanged()
      • 4.2 onNewIntent()
    • 五. onSaveInstanceState()和onRestoreInstanceState()
    • 六. Activiy是如何啟動的
      • 6.1 Zygote的概念與作用
      • 6.2 SystemServer的概念與作用
      • 6.3 AMS概念、初始化、作用
      • 6.4 Launcher概念,啟動時機
      • 6.5 Instrumentation與ActivityThread的關系
      • 6.6 ActivityThread與AMS之間的Binder通信
      • 6.7 AMS如何開啟Activity
      • 本部分代碼見 [AndroidPokkit](https://github.com/leidongld/AndroidPokkit/tree/master/app/src/main/java/com/openld/androidpokkit)中的chapter1

一.Activity概念

移動應用體驗與桌面體驗的不同之處在于,使用者與應用的互動并不總是在同一位置開始,而是經常以不确定的方式開始。例如,如果您從主螢幕打開電子郵件應用,可能會看到電子郵件清單,如果您通過社交媒體應用啟動電子郵件應用,則可能會直接進入電子郵件應用的郵件撰寫界面。

Activity 類的目的就是促進這種範式的實作。當一個應用調用另一個應用時,調用方應用會調用另一個應用中的 Activity,而不是整個應用。通過這種方式,Activity 充當了應用與使用者互動的入口點。您可以将 Activity 實作為 Activity 類的子類。

Activity 提供視窗供應用在其中繪制界面。此視窗通常會填滿螢幕,但也可能比螢幕小,并浮動在其他視窗上面。通常,一個 Activity 實作應用中的一個螢幕。例如,應用中的一個 Activity 實作“偏好設定”螢幕,而另一個 Activity 實作“選擇照片”螢幕。

大多數應用包含多個螢幕,這意味着它們包含多個 Activity。通常,應用中的一個 Activity 會被指定為主 Activity,這是使用者啟動應用時出現的第一個螢幕。然後,每個 Activity 可以啟動另一個 Activity,以執行不同的操作。例如,一個簡單的電子郵件應用中的主 Activity 可能會提供顯示電子郵件收件箱的螢幕。主 Activity 可能會從該螢幕啟動其他 Activity,以提供執行寫郵件和打開郵件這類任務的螢幕。

雖然應用中的各個 Activity 協同工作形成統一的使用者體驗,但每個 Activity 與其他 Activity 之間隻存在松散的關聯,應用内不同 Activity 之間的依賴關系通常很小。事實上,Activity 經常會啟動屬于其他應用的 Activity。例如,浏覽器應用可能會啟動社交媒體應用的“分享”Activity。

要在應用中使用 Activity,您必須在應用的清單中注冊關于 Activity 的資訊,并且必須适當地管理 Activity 的生命周期。本文的後續内容将介紹這些主題。

二.Activity生命周期

此處放一張圖,無須再贅述什麼。

【Android重點知識突破系列1】——Activity

當一個Actiivity新建立時,其生命周期如下:

【Android重點知識突破系列1】——Activity

當一個Activity關閉時,其生命周期如下:

【Android重點知識突破系列1】——Activity

當一個Activity退到背景時,其生命周期如下:

【Android重點知識突破系列1】——Activity

當一個Activity由背景回到前台時,其生命周期如下:

【Android重點知識突破系列1】——Activity

當橫豎屏切換時,其生命周期如下:

【Android重點知識突破系列1】——Activity

三.啟動模式

3.1 Activity四種啟動模式

3.1.1 基本概念

  • standard

建立一個Activity就在頁面棧中入棧一個Activity。

  • singleTop

如果目前Activity已經在棧頂,重複建立該Activity時不會建立,而是複用已有的在棧頂的Activity。如果目前Activity不在棧頂,則會建立執行個體。

  • singleTask

棧内隻允許有唯一的Activity執行個體,再次重複建立時會把該Activity上方的其他Activity踢出棧GC掉,該Activity此時也就頂到了棧頂。

  • singleInstance

新起一個任務棧來存新的Activity執行個體。

3.1.2 taskAffinity屬性

taskAffnity翻譯為任務相關性,該參數辨別了一個Activity所需要的任務的名字。在預設情況下,所有Activity所需的任務的名字都是應用的包名,但是我們可以為Activity單獨設定所需棧的名字。taskAffnity屬性不能設定為包名,否則相當于無效設定;taskAffnity隻能和singleTask啟動模式搭配使用,在其他情況下設定沒有意義。

3.1.3 Intent中标志位設定啟動模式

- FLAG_ACTIVITY_CLEAR_TOP

如果設定該屬性,并且正在啟動的Actvity已在目前任務中運作,那麼與其啟動該Activity的新執行個體,不如關閉該Activity之上的所有其他Activity,并将此Intent傳遞給将舊活動(現在頂部)作為新的Intent。

例如,考慮一個由以下Activity組成的任務:A,B,C,D。如果D調用具有解析為Activity B元件的Intent的startActivity(),則C和D将完成,并且B接收給定的Intent ,則堆棧現在為:A,B。

在上面的示例中,Activity B的目前正在運作的執行個體将通過onNewIntent()方法接收在此處開始的新Intent,或者自行finish并使用新Intent重新啟動。如果已将其啟動模式聲明為預設,并且未将FLAG_ACTIVITY_SINGLE_TOP設定到同一Intent,則它将finish并重新建立;對于所有其他啟動模式,或者如果設定了FLAG_ACTIVITY_SINGLE_TOP,則此Intent将被傳遞到目前執行個體的onNewIntent()。

此啟動模式還可以與FLAG_ACTIVITY_NEW_TASK結合使用,以達到良好效果:如果用于啟動任務的根Activity,它将把該任務的任何目前正在運作的執行個體置于前台,然後将其清除為根狀态。例如,當從通知管理器啟動活動時,此功能特别有用。

- FLAG_ACTIVITY_SINGLE_TOP

如果設定了該屬性,則如果該Activity已經在任務頂運作,則不會啟動該活動。類似于launchMode的singleTop。

  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

如果設定,則新Activity不保留在最近啟動的Activity清單中。

- FLAG_ACTIVITY_NO_HISTORY

如果設定,則新Activity不會保留在曆史記錄堆棧中。 一旦使用者離開它,Activity就結束了。 也可以使用noHistory屬性設定。

如果設定,則在目前Activity開始一個新Activity(設定結果并完成)時,永遠不會調用onActivityResult()。

- FLAG_ACTIVITY_NEW_TASK

如果設定,此Activity将成為此曆史記錄堆棧上新任務的開始。任務(從啟動它的Activity到下一個任務Activity)定義了使用者可以移動到的原子Activity組。任務可以移到前台和背景;特定任務内的所有Activity始終保持相同的順序。

希望呈現“launcher”樣式行為的Activity通常使用此标志:它們為使用者提供了可以完成的單獨操作的清單,否則,這些操作完全獨立于啟動它們的Activity而運作。

使用此标志時,如果你正在啟動的Activity恰好正在任務中運作,那麼将不會啟動新的Activity;取而代之的是,目前任務将以其上次進入的狀态簡單地顯示在前台。

當調用者從正在啟動的Activity中請求結果時,不能使用此标志。

在Service中啟動Activity時要加該屬性,否則會報異常。

3.1.4 startActivity場景

在應用中打開新的Activity基本可以分為以下兩種情況。

- 目标Activity就在本應用中

直接在Manifest的launchMode屬性中靈活配置,任務棧也可以随意配置。

- 目标Activity在第三方應用中

要優先考慮新打開的Activity是和自己的App放在同一個任務棧中還是新起一個任務棧。然後再考慮Activity的啟動模式,通過Intent.setFlags()來配置。

3.2 Activity四種啟動模式的使用場景

- standard

不配置就是預設該模式

- singleTop

登入頁面、WXEntryActivity、WXPayEntryActivity、推送通知欄頁

- singleTask

支付頁面、邏輯首頁面、WebView頁面、付款頁面、确認訂單頁面

- singleInstance

系統launcher、鎖屏頁面、來電顯示頁面

3.3 Intent

Intent 是一個消息傳遞對象,消息大小是有限制的,不要接近或超過1M。可以用來從其他應用元件請求操作。盡管 Intent 可以通過多種方式促進元件之間的通信,但其基本用例主要包括以下三個:

- 啟動 Activity

Activity 表示應用中的一個螢幕。通過将 Intent 傳遞給 startActivity(),可以啟動新的 Activity 執行個體。Intent 用于描述要啟動的 Activity,并攜帶任何必要的資料。

如果希望在 Activity 完成後收到結果,請調用 startActivityForResult()。在 Activity 的 onActivityResult() 回調中, Activity 将結果作為單獨的 Intent 對象接收。

- 啟動服務

Service 是一個不使用使用者界面而在背景執行操作的元件。

對于 Android 5.0(API 級别 21)之前的版本,您可以使用 Service 類的方法來啟動服務。通過将 Intent 傳遞給 startService(),可以啟動服務執行一次性操作(例如,下載下傳檔案)。Intent 用于描述要啟動的服務,并攜帶任何必要的資料。

如果服務旨在使用用戶端-伺服器接口,則通過将 Intent 傳遞給 bindService(),可以從其他元件綁定到此服務。

- 傳遞廣播

廣播是任何應用均可接收的消息。系統将針對系統事件(例如:系統啟動或裝置開始充電時)傳遞各種廣播。通過将 Intent 傳遞給 sendBroadcast() 或 sendOrderedBroadcast(),可以将廣播傳遞給其他應用。

3.3.1 Intent屬性

- 元件名稱(ComponentName)

這是可選項,但也是建構顯式 Intent 的一項重要資訊,這意味着 Intent 應當僅傳遞給由元件名稱定義的應用元件。Intent 的這一字段是 ComponentName 對象,可以使用目标元件的完全限定類名指定此對象,其中包括應用的軟體包名稱。

- 操作(Action)

指定要執行的通用操作(例如,“檢視”或“選取”)的字元串。

- 資料(Data)

引用待操作資料和/或該資料 MIME 類型的 URI(Uri 對象)。提供的資料類型通常由 Intent 的操作決定。

(要僅設定資料 URI,請調用 setData()。要僅設定 MIME 類型,請調用 setType()。如有必要,可以使用 setDataAndType() 同時顯式設定二者。

警告:若要同時設定 URI 和 MIME 類型,請勿調用 setData() 和 setType(),因為它們會互相抵消彼此的值。請始終使用 setDataAndType() 同時設定 URI 和 MIME 類型。)

- 類别(Category)

一個包含應處理 Intent 元件類型的附加資訊的字元串。可以将任意數量的類别描述放入一個 Intent 中,但大多數 Intent 均不需要類别。

- Extra

攜帶完成請求操作所需的附加資訊的鍵值對。正如某些操作使用特定類型的資料 URI 一樣,有些操作也使用特定的附加資料。例如,使用 ACTION_SEND 建立用于發送電子郵件的 Intent 時,可以使用 EXTRA_EMAIL鍵指定“目标”收件人,并使用 EXTRA_SUBJECT 鍵指定“主題”。

- 标志(Flags)

在 Intent 類中定義的、充當 Intent 中繼資料的标志。标志可以訓示 Android 系統如何啟動Activity(例如,Activity 應屬于哪個Task )。

3.3.2 Intent的顯式與隐式

- 顯式 Intent

通過提供目标應用的軟體包名稱或完全限定的元件類名來指定可處理 Intent 的應用。通常,您會在自己的應用中使用顯式 Intent 來啟動元件,這是因為您知道要啟動的 Activity 或服務的類名。例如,您可能會啟動您應用内的新 Activity 以響應使用者操作,或者啟動服務以在背景下載下傳檔案。

// Executed in an Activity, so 'this' is the Context
// The fileUrl is a string URL, such as "http://www.example.com/image.png"
val downloadIntent = Intent(this, DownloadService::class.java).apply {
    data = Uri.parse(fileUrl)
}
startService(downloadIntent)
           

- 隐式 Intent

不會指定特定的元件,而是聲明要執行的正常操作,進而允許其他應用中的元件來處理。例如,如需在地圖上向使用者顯示位置,則可以使用隐式 Intent,請求另一具有此功能的應用在地圖上顯示指定的位置。

// Create the text message with a string
val sendIntent = Intent().apply {
    action = Intent.ACTION_SEND
    putExtra(Intent.EXTRA_TEXT, textMessage)
    type = "text/plain"
}

// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(packageManager) != null) {
    startActivity(sendIntent)
}
           

3.4 Activity的異常生命周期

3.4.1 系統配置改變引起異常

在Android中,系統配置改變時會引起Activity銷毀重建的(改了權限或者字型等系統配置後,一定要注意頁面重建可能存在的風險與隐患)。在重建的流程中,原來的Activity會在銷毀前調用onSaveInstanceState()儲存狀态。建立Activity後會調用onRestoreInstanceState()恢複狀态。

【Android重點知識突破系列1】——Activity

上面是改字型的一個例子。當打開系統設定,此時我們的應用Activity走了1中的生命周期方法。确實修改了字型配置後再次打開App,走了2中的生命周期方法。

3.4.2 系統回收引起異常

在Android系統記憶體不足且失去焦點被系統回收時會調用onSaveInstanceState()儲存狀态,Activity再次建立時,會調用onRestoreInstanceState()恢複狀态。

3.4.3 異常基礎處理

  • 在onCreate()時判斷saveInstanceState是否為空,不為空就可以從中恢複資料。
  • 根據應用場景,可以在Manifest中配置各種參數盡可能地減少由于配置參數導緻的Activity異常。
  • 使用onConfigurationChanged()方法代替onRestoreInstanceState()實作恢複資料邏輯。

四. onConfigurationChanged()和onNewIntent()

4.1 onConfiguratonChanged()

當系統的配置資訊發生變化時,系統會調用該方法。但是要注意,隻有在Manifest檔案中的對應Activity的configChanges中設定了要處理的系統配置變更類型時,該方法才會被調用。如果發生變更的配置與configChanges中設定的屬性不比對,那麼Activity會被銷毀并使用新的配置重建。

比如螢幕旋轉前你已經在configChanges中配置了

【Android重點知識突破系列1】——Activity

那麼你的Activity并不會被重建,而是調用onConfigurationChanged方法。

【Android重點知識突破系列1】——Activity

4.2 onNewIntent()

當啟動模式為singleTop時,Activity執行個體目前在棧頂時,此時會調用onNewIntent方法,調用順序為:onPause—>onNewIntent—>onResume。

【Android重點知識突破系列1】——Activity

當啟動模式為SingleInstance和singleTask時,若Activity已在任務棧時,就會調用onNewIntent方法,調用順序為:onPause—>onNewIntent—>onRestart—>onStart—>onResume。

是以,隻有SingleTop(位于棧頂),SingleTask和SingleInstance(且棧中已存在執行個體),再次啟動它們時才會調用,僅從背景切換到前台而不再次啟動的情況下不會觸發onNewIntent。

【Android重點知識突破系列1】——Activity

五. onSaveInstanceState()和onRestoreInstanceState()

如果由于系統限制(而非正常的應用程式行為)導緻Activity被破壞,那麼盡管實際上Activity已經銷毀,但是系統還是會記住它曾經存在。此時使用者重新導航回它,系統會建立一個新的Activity,使用儲存的狀态資料來描述Activity在被銷毀時的狀态。

【Android重點知識突破系列1】——Activity

儲存和恢複狀态的方法是onSaveInstanceState()和onRestoreInstanceState(),隻有在Activity被意外銷毀的時候這兩個方法才會觸發調用,一般的Home鍵退到首頁面再回來的時候是觸發不到這兩個方法的。

六. Activiy是如何啟動的

6.1 Zygote的概念與作用

6.2 SystemServer的概念與作用

6.3 AMS概念、初始化、作用

6.4 Launcher概念,啟動時機

6.5 Instrumentation與ActivityThread的關系

6.6 ActivityThread與AMS之間的Binder通信

6.7 AMS如何開啟Activity

本部分代碼見 AndroidPokkit中的chapter1