開發藝術探索——第一章:Activity的生命週期和啟動模式
目錄
知識點:
引言:本章的重點是Activity 在使用過程中的一些不容易搞清楚的概念,主要包括生命週期和啟動模式以及IntentFilter的匹配規則分析。
1.1 Activity 的生命週期和啟動模式
本章將Activity的生命週期分為兩部分內容,一部分是典型情況下的生命週期,另一部分是異常情況下的生命週期。
1.1.1 典型情況下的生命週期分析
對於Activity的生命週期 相信大家已經是 牢記於心,對此,不在介紹每個方法所涵蓋的內容了。
請繼續往下看。
分析幾種常見情況:
- 針對一個特定的Activity,第一次啟動,回撥如下:onCreate - onStart - onResume
- 當用戶開啟新的Activity或者切換到主介面時,回撥如下:onPause - onStop,這裡有一種特殊的情況,如果新Activity 採用了透明主題,那麼當前Activity 不會回撥onStop
- 當用戶再次回到原Activity時,回撥如下:onRestart - onStart - onResume
- 當用戶按back 鍵回退時,回撥如下:onPause - onStop - onDestory
- Activity被系統回收後再次開啟 生命週期和 1 一樣,不代表所有過程都一樣
- 從整個生命週期來說 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" />