1.1 Activity 的生命周期全面分析
1.1.1 典型情況下的生命周期分析
- onPause: 正在停止,正常情況下緊接著 onStop 就會被調用,然后新的 Activity 執行 onResume; 如果新 Activity 采用了透明主題,則不會調用 onStop,因為 onStop 意味著不可見
- 按 back 鍵回退時,回調 onPause -> onStop -> onDestroy
- 生命周期的配對
- onCreate 與 onDestroy,創建和銷毀
- onStart 與 onStop,顯示與消失
- onResume 與 onPause,前臺與后臺
- 舊的活動 onPause,然后新的活動 onResume
1.1.2 異常情況下的生命周期分析
- 資源相關的系統配置發生改變導致 Activity 被殺死并重啟(比如橫豎屏切換),這時候會在 onDestroy 前調用 onSaveInstanceState,在 onCreate 后調用 onRestoreInstanceState。要注意兩點問題:
- 只有在異常情況(包括第二種情況)下 onSaveInstanceState 和 onRestoreInstanceState 才會被調用
- onRestoreInstanceState 里的 Bundle 參數無需判空,而 onCreate 的 Bundle 參數就需要。
- 內存不足導致低優先級的 Activity 被殺死
指定在某種情況下,系統不會重啟 Activity,可以給 configChanges 屬性添加值,常見的有:
項目 | 含義 |
---|---|
locale | 切換了系統語言 |
orientation | 屏幕方向變化 |
keyboardHidden | 鍵盤的可訪問性發生了改變,比如用戶調出了鍵盤 |
screenSize | api 13 以上(min 和 target 大于13)屏幕方向切換不會重啟,否則重啟 |
1.2 Activity 的啟動模式
1.2.1 Activity 的 LaunchMode
四種啟動模式,解決了一下幾個問題:
- 單個任務棧下重復創建某個 Activity,創建新的實例還是復用已有的實例
- Activity 的復用策略,是棧頂復用還是棧內復用
- 使用 TaskAffinity 屬性配置多任務棧,當任務棧中的一個活動被喚醒,任務棧也恢復到前臺狀態
標準模式,重新創建目標 Activity,并加入啟動它的 Activity 的任務棧中,所以非 Activity 類型的 Context 啟動標準模式的 Activity 會報錯,這時只要指定 FLAG_ACTIVITY_NEW_TASK 標記位
singleTop,棧頂復用,啟動棧頂的 Activity 不會重新創建,且 onNewIntent 方法會被調用
singleTask,棧內復用
- 如果要啟動的 Activity 需要的任務棧(根據 TaskAffinity 屬性查找)沒有找到,系統會為它創建該任務棧,并將 Activity 放到棧中
- 如果需要的任務棧存在,且存在該 Activity 的實例,則該 Activity 不會重新創建,且 onNewIntent 方法會被調用; 如果所需的棧中存在該 Activity,可以指定 FLAG_ACTIVITY_CLEAR_TOP 標記位,則該 Activity 上的 Activity 全部出棧。
- 單獨講講 TaskAffinity 屬性,它標識了一個 Activity 所需要的任務棧的名字,默認(即不配置該屬性的話)為應用的包名,通常與 singleTask 模式或 allowTaskReparenting 屬性搭配使用:
- 當與 singleTask 模式配對使用時,如前文所述;
- 當與 allowTaskReparenting 屬性配對使用時,比如當應用 A 啟動了 應用 B 的某個 Activity,若該 Activity 的 allowTaskReparenting 設置為 true,則當應用 B 啟動時,此 Activity 會直接從應用 A 的任務棧轉移到 應用 B 的任務棧中。
- singleInstance,單實例模式,啟動時系統直接為其創建新的任務棧(無需配置 TaskAffinity),也就是永遠不會重復創建該活動的實例
使用 adb shell dumpsys activity 命令查看設備的任務棧
1.2.2 啟動 Activity 的 Flags(Intent 的標志位)
通過 intent.setFlags(Intent.FLAG_ACTIVITY_*) 來動態指定啟動模式
標記 | 作用 |
---|---|
FLAG_ACTIVITY_NEW_TASK | 使用 singleTask 模式 |
FLAG_ACTIVITY_SINGLE_TOP | 使用 singleTop 模式 |
FLAG_ACTIVITY_CLEAR_TOP | 位于它上面的 Activity 出棧,與 singleTask 搭配使用 |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 不保留此Activity 的啟動歷史,等效在 XML 中指定 android:excludeFromRecents="true" |
1.3 IntentFilter 的匹配規則
用于 Activity 的隱式啟動,例如你點擊一個 http url,系統會彈出對話框要你選擇使用哪一種瀏覽器;IntentFilter 的過濾信息有 action,category,data。
<activity android:name=".ui.MainActivity">
<intent-filter>
<action android:name="**" />
<category android:name="**" />
<data
android:scheme="**"
android:host="**"
android:port="**"
android:path="**"
android:pathPattern="**"
android:pathPrefix="**"
android:mimeType="**" />
</intent-filter>
</activity>
action 表示一個 Activity 能干什么
比如發送,共享,打電話等等;一個 action 表示一個動作,一個 Intent 中會加入若干個 action,表示它要干什么,沒有同時具備這些能力的 activity 會被過濾掉;如果一個 Intent 沒有 action,那沒人知道它要干什么,也就無法匹配這個 Intent。category 表示一個 Activity 是什么
比如可以標記一個 Activity 是地圖,瀏覽器,日歷等等;一個 Intent 中不加 category,系統也會給他一個默認的 category,即 category.DEFAULT;其他情況也是與 action 類似的。data 表示一個 Activity 可以響應那些類型的資源,具體就是 URI;
別看 data 的屬性那么多,其實可以分為兩個部分,mimeType 和 URI;一個 URI 的結構就是
<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
若沒有主動指定 URI,那么默認就是 content 和 file,即本地文件。
例如百度的 APP 會對 www.baidu.com 的鏈接響應,MP3
播放器會對 MP3 格式的文件響應,迅雷下載器會對特定的 URI 響應等等。