1. 程式人生 > >開發藝術探索——第一章:Activity的生命週期和啟動模式

開發藝術探索——第一章:Activity的生命週期和啟動模式

目錄

1.1 Activity 的生命週期和啟動模式

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

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

1.2 Activity 的啟動模式

1.2.1 Activity 的 LaunchMode

1.2.2 Activity 的Flags

1.3 IntentFilter 的匹配規則


知識點:

引言:本章的重點是Activity 在使用過程中的一些不容易搞清楚的概念,主要包括生命週期和啟動模式以及IntentFilter的匹配規則分析。

1.1 Activity 的生命週期和啟動模式

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

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

對於Activity的生命週期 相信大家已經是 牢記於心,對此,不在介紹每個方法所涵蓋的內容了。

請繼續往下看。

分析幾種常見情況:

  1. 針對一個特定的Activity,第一次啟動,回撥如下:onCreate - onStart - onResume
  2. 當用戶開啟新的Activity或者切換到主介面時,回撥如下:onPause - onStop,這裡有一種特殊的情況,如果新Activity 採用了透明主題,那麼當前Activity 不會回撥onStop
  3. 當用戶再次回到原Activity時,回撥如下:onRestart - onStart - onResume
  4. 當用戶按back 鍵回退時,回撥如下:onPause - onStop - onDestory
  5. Activity被系統回收後再次開啟 生命週期和 1 一樣,不代表所有過程都一樣
  6. 從整個生命週期來說 onCreate 和 onDestory 是配對的,從是否可見來說 onStart 和 onStop 是配對的 ,從Activity 是否在前臺來說 onResume 和 onPause 是配對的。

問題:

Ask: onStart 和 onResume 、onPause 和 onStop 從描述來說差不多,對我們來說有什麼實質的不同?

answer:onStart 和 onResume 是從Activity 是否可見這個角度來回調的,而onPause 和 onStop 是否在前臺這個角度回撥的,在實際使用中沒有其他明顯的區別。

Ask:當從A開啟 B,那麼B 的onResume 和 A 的onPause 哪個先執行呢?

answer:書中分析了原始碼,感興趣的可以一觀。 確定為 A 的onPause 先執行。

週期:A- onPause  B-onCreate  B- onStart B - onResume A- onStop

Activity的啟動過程 簡單理解: 啟動Activity 的請求會由 Instrumentation 來處理,然後通過它的Binder向AMS發請求,AMS內部維護著一個ActivityStack並負責棧內的 Activity 的狀態同步,AMS通過ActivityThread 去同步Activity的狀態從而完成生命週期的回撥。

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

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

比如橫豎屏切換時,預設情況下,Activity就會被銷燬並且重建,如果不作處理 生命週期如下:

onSaveInstanceState - onDestory 重新建立 onCreate onRestoreInstanceState

onSaveInstanceState  發生在onStop之前 他和onPause 沒有既定的時序關係。

情況2:資源記憶體不足導致低優先順序的Activity 被殺死

資料儲存和恢復過程和情況 1 完全一致。

當資源記憶體不足時 將會先殺掉 優先順序低 的Activity。

配置Activity相關屬性,使Activity 在某些情況下不會重新建立

如 configChanges 屬性新增 android:configChanges="orientation" 可避免 Activity 在螢幕旋轉時重新建立。

若想指定多個值,可用“|”連線起來,如 andorid="orientation|keyboardHidden"

以下為大多數配置內容

常用的只有 locale、orientaion和keyboardHidden 這三個選項,需要注意的是screenSize 和 smallestScreenSize,它們兩個比較特殊,它們的行為和編譯選項有關,但和執行環境無關。

1.2 Activity 的啟動模式

1.2.1 Activity 的 LaunchMode

standard:標準模式,沒啟動一個Activity 都會重新建立一個新的例項,在這種模式下,誰啟動了這個Activity,那麼這個Activity就執行在啟動它的那個Activity所在的棧中。 注意:由於非Activity的Context(如ApplicationContext)並沒有所謂的任務棧,所以就有問題了。解決辦法:指定 FLAG_ACTIVITY_NEW_TASK 標記位。

singleTop:棧頂複用模式。如果新建立的 Activity 在棧頂,那麼此Activity不會被重新建立,同時它的onNewIntent方法會被重新呼叫,週期:onCreate、onStart不會呼叫。

singleTask:棧內複用模式。這是一種單例項模式,只要Activity在一個棧中存在,那麼將和singleTop一樣,不會被重新建立,也會回撥onNewIntent。同時會導致所有在此Activity之上的Activity全部出棧。

singleInstance:一種加強的 singleTask 模式,具有 singleTask 模式的所有特性,並且具有此種模式的 Activity 只能單獨地位於一個任務棧中。

Activity 指定啟動模式有兩種方法:

通過 AndroidManifest.xml為 Activity 指定啟動模式

通過在 Intent 中設定標誌位為 Activity 指定啟動模式。

二者區別在於:

優先順序上,方式 2 的優先順序高於方式 1,當兩種同時存在時,以方式 2 為準;

限定範圍不同,比如,方式 1 無法直接為 Activity 設定 FLAG_ACTIVITY_CLEAR_TOP 標識,而方式 2 無法為 Activity 指定singleInstance 模式。

另外還有點就是,在singleTask模式中,多次提到 Activity 所需的任務棧,那麼這個任務棧是什麼呢?這就要從一個引數說起:TaskAffinity,可以翻譯為任務相關性,標識了一個Activity 所需要的任務棧的名字。我們可以為每個Activity 設定單獨的TaskAffinity ,當然這個屬性值不能設定成包名,否則就相當於沒有指定。
TaskAffinity屬性主要是和SingleTask模式或者allowTaskReparenting屬性配對使用。那麼allowTaskReparenting 的作用是什麼呢?比如,從A程式 打開了B程式的 某個Activity ,如果這個Activity 的allowTaskReparenting 為 true的話,那麼當應用B 被啟動後將會顯示B 的 某個Activity 介面。

1.2.2 Activity 的Flags

Activity 的 Flags 有很多,有的標記位可以設定 Activity 的啟動模式,有的會影響 Activity 的執行狀態等。常用的如下:

FLAG_ACTIVITY_NEW_TASK

其作用是為 Activity 指定 singleTask 啟動模式,效果和在 XML 中指定該模式相同。

FLAG_ACTIVITY_SINGLE_TOP

其作用是為 Activity 指定 singleTop 啟動模式,效果和在 XML 中指定該模式相同。

FLAG_ACTIVITY_CLEAR_TOP

具有此標記位的 Activity 啟動時,在同一個任務棧中所有位於它上面的 Activity 都要出棧。

此模式一般需要和 FLAG_ACTIVITY_NEW_TASK 配合使用,若被啟動Activity 的例項已存在,則系統會呼叫它的 onNewIntent

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

具有此標記的 Activity 不會出現在歷史 Activity 的列表中(用於某些情況不希望使用者通過歷史列表回到 Activity 時)。它等同於在 XML 中指定 Activity 的屬性 android:excludeFromRecents="true"。

1.3 IntentFilter 的匹配規則

啟動Activity 分為兩種,顯式呼叫和隱式呼叫,顯式呼叫需要明確地指定被啟動物件的元件資訊,包括類名和包名,而隱式呼叫需要明確的元件資訊。這裡我們主要介紹一下隱式呼叫。

隱式呼叫需要Intent能夠匹配目標元件的IntentFilter 中所設定的過濾資訊。

IntentFilter的過濾資訊有action、category、data。為了匹配過濾列表,需同時匹配過濾列表中的action、category、data資訊,否則匹配失敗;一個Activity中可以有多個intent-filter,一個Intent只要能匹配任何一組intent-filter即可成功啟動對應的Activity。

action的匹配規則

一個過濾規則中可以有多個action,Intent中的action能夠和過濾規則中的任何一個action相同即可匹配成功。

如果Intent沒有指定action,那麼匹配失敗。action區分大小寫。

category的匹配規則

Intent中可以沒有category;如果有,不管有幾個,每個都要能和過濾規則中的任何一個category相同;

如果沒有,依然可以匹配成功,原因是因為如果沒有指定category,在呼叫startActivity或startActivityForResult時系統會預設加上“android.intent.category.DEFAULT”這個category。

同時為了我們的Activity能夠支援隱式呼叫,就必須要在intent-filter中指定“android.intent.category.DEFAULT”這個category。

data的匹配規則

data的匹配規則和action類似,如果過濾規則定義了data,那麼Intent必須定義可匹配的data。

data由兩部分組成,mimeType和URI。mimeType是指媒體型別,比如image/jpeg、audio/mpeg4-generic和video/等,可以表示圖片、文字、視訊等不同的媒體格式,而URI中包含的資料就比較多了,下面是URI的結構*:

<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
//實際例子
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info

Scheme:URI的模式,比如http、file、content等,如果URI中沒有指定scheme,那麼整個URI的其他引數無效,也意味著URI是無效的。

Host:URI的主機名,比如www.baidu.com,如果host未指定,那麼整個URI中的其他引數無效,也以為著URI無效。

Port:URI中的埠號,比如80,僅當URI中指定了scheme和host引數的時候port引數才是有意義的。

Path、pathPattern和pathPrefix:這三個引數都是表示路徑資訊;其中path表示完整的路徑資訊;pathPattern也表示完整路徑資訊,但是它裡面可以包含萬用字元“”,“”表示0個或多個任意字元;pathPrefix表示路徑的字首資訊。

Tips:

1. 當我們隱式啟動一個Activity的時候,可以做一下判斷,看是否能匹配到我們的隱式Intent,如果不做判斷沒找到對應的Activity系統就會丟擲android.content.ActivityNotFoundException異常。

方法:採用PackageManager的resolveActivity方法或者Intent的resolveActivity方法,如果找不到匹配的Activity就會返回null,我們通過判斷返回值就可以規避上述錯誤了。

2. 在intent-filter中聲明瞭<category android:name="android.intent.category.DEFAULT"/>這個category的Activity,才可以接收隱式意圖。

3. 有一類action和category的共同作用是標明這是一個入口Activity,並且會出現在系統的應用列表中,少一個都沒有任何意義,也不會出現在系統的應用列表中。

<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />