第一章?? Activity的生命周期和啟動模式
書中作者大體上從兩種情況來介紹了Activity的生命周期。第一種是手機運行環境良好不變(屏幕方向不變、內存充足等),用戶正常操作APP頁面。第二種情況是異常情況下,Activity的生命周期受到了運行環境改變的影響,被系統殺死,Activity的生命周期發生了改變。
在第一種情況中,Activity的生命周期大致如下:
系統調用Activity的onCreate方法創建一個Activity對象,隨后調用onStart方法,這時候Activity其實就是可見狀態,但還不是可交互狀態,接下來會調用onResume方法將其作為前臺活動頁面,此時Activity的狀態是可交互狀態,此過程對應的是圖一中的綠色箭頭所顯示的流程。
當出現頁面部分被遮擋從而使用戶無法與此Activity進行交互時(如:頁面中彈出了對話框遮擋住了Activity),Activity的onPause方法就會被調用。如果對話框消失,Activity重新回到前臺狀態(可交互狀態),那么onResume方法會重新被調用。此過程是圖一中藍色箭頭所顯示的流程。
如果Activity A正處于可交互狀態,此時頁面跳轉到另一個頁面Activity B,生命周期函數會依次被調用:Activity A 的onPause----Activity B 的onCreate-------Activity B的onStart------Activity B 的onResume-------Activity A 的onStop。當頁面再次回到Activity A的時候,會調用Activity B的onPause-----------Activity A的onRestart-------Activity A的 onStart--------Activity A 的onResume------Activity B的onStop。此過程是圖一紫色箭頭所顯示的流程。
在第二種情況中,也分兩種異常情況。
第一種異常情況是指與資源相關的系統配置發生變化,如將手機語言進行更改,或者是手機的屏幕方向發生了改變,都會使Activity被重新創建加載,除非Activity中configChanges屬性配置了對應的值。在異常情況下,Activity發生被回收重新創建,其onPause、onStop、onDestory方法均被調用。同時在調用onStop方法之前還調用了另一個方法onSaveInstanceState方法將頁面的狀態進行保存(至于如何對Activity狀態進行保存就需要去閱讀相關源碼才知道,不過書中提到系統是運用了委托機制來進行狀態的保存)。在該Activity再次被創建時,系統會調用onRestoreInstanceState方法,并將onSaveInstanceState方法中保存的Bundle對象來作為參數傳遞給onRestoreInstanceState方法,此過程如圖二所示。
第二種異常情況,當手機運行內存不足會導致優先級別較低的Activity所在的進行被殺死。Activity按照優先級別從高到低可以分為:前臺交互的Activity優先級別最高、其次是可見但無法交互的Activity、最低級別是處于系統后臺被暫停的Activity。在手機內存不足的情況下,系統會優先殺死優先級別低的Activity所在進程。同樣的,在此過程中Activity的生命周期跟第一種異常情況是一樣的,此過程如圖一紅色箭頭所示。
Activity的啟動模式
書中分別介紹了Activity的四種加載模式:standard、singleTop、singleTask、singleInstance。
第一種加載方式相對容易理解,在標準模式下啟動,被啟動的Activity將會加載到啟動它的Activity所在的任務棧中,所以如果被加載的Activity不是由Activity的context來啟動加載的,那么就會報錯,書中舉了ApplicationContext通過標準模式下加載Activity就會報異常這個例子。此模式下Activity如果多次被加載,會出現多個實例。
第二種加載模式singleTop是指棧頂復用。意思就是如果Activity的實例是位于棧頂,再次以棧內復用的方式加載的話,該Activity不會被重新創建出新的實例,而是重復利用已經位于棧頂的實例,所以被重用的實例都會在被重新調用的時候加載它的onNewIntent方法。如果將要被啟動的Activity的實例不是位于棧頂,那么還是會被重新創建新的實例。
第三種Activity加載方式singleTask是指棧內復用。棧內復用是指Activity實例所需要的任務棧中已經存在一個將要被加載的Activity的實例,那么該實例將會被復用。同時,如果實例被重用,所有位于該實例頂的實例都會被清出該任務棧,符合clearTop原則。跟singleTask配合使用的還有一個Activity的屬性,TaskAffinity。該屬性可以指定加載的Activity到哪個任務棧。默認情況下所有的Activity都是加載都以包名相同的任務棧中。
第四種加載模式singleInstance。這是一種比較少用到的模式,在全局中單例模式。意思就是在整個應用中,如果某個Activity以這種方式加載啟動,那么系統會新創建一個任務棧,并且創建一個Activity的實例入棧。
學完這四種加載模式后,我最后有一個疑問,如果說在標準模式下啟動,被啟動的Activity將會加載到啟動它的Activity所在的任務棧中,那如果從頁面MainActivity標準方式跳轉到SecondActivity,再通過SinlgeInstance方式加載SingleInstanceActivity,最后在SingleInstanceActivity中通過標準的加載方式加載ThirdActivity,那么ThirdActivity會不會跟SingleInstanceActivity在同一個任務棧中呢?
下圖為我自己驗證一個過程的結果:
顯然ThirdActivity不跟SingleInstanceActivity在同一個任務棧,而是在默認的任務棧中。
最后談到了設置Activity加載模式的方法有兩種,一種是通過清單文件來設置,另外一種是通過Java代碼中啟動跳轉的時候設置標示位來實現,兩種方式的區別不是很大,但第一種方式無法設置FLAG_ACTIVITY_CLEAR_TOP標識,第二種方法無法為Activity設置singleInstance模式,并且如果兩種方式都設置了,那么以第二種方式為主。
Activity的Flags
這部分內容比較少使用到,正常情況下都不需要指定Activity的Flags。書中主要介紹了常見的幾種標識:
FLAG_ACTIVITY_NEW_TASK??? =====作用等同于啟動模式中的singleTask模式
FLAG_ACTIVITY_SINGLE_TOP======作用等同于啟動模式中的singleTop模式
FLAG_ACTIVITY_CLEAR_TOP?? ======該標識需要FLAG_ACTIVITY_NEW_TASK配合使用。將目標Activity實例所在任務棧中位于該實例上面的所有Activity都被清空退出棧內。意味中singleTask模式就具有FLAG_ACTIVITY_CLEAR_TOP標識
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS=======具有該標識的Activity不會出現歷史Activity列表中。該標識等同于Activity屬性android:excludeFromRecents=true
IntentFilter的匹配規則
啟動Activity的方式包括了顯式和隱式,顯示啟動相對簡單,這里主要是講解如何通過IntentFilter的匹配來隱式啟動Activity。在清單文件中,Activity標簽中可以包含多個IntentFilter匹配規則。每個匹配規則中都是由action、category、data三個標簽來定義規則。只有完全匹配到某一個IntentFilter,才會啟動對應的Activity。如何才算完全匹配到呢?在IntentFilter中的所定義的屬性都分別滿足相應的action、category、data的匹配規則。
1)action的匹配規則:
action的字符串值必須完全一樣,區分字母的大小寫。如果一個IntentFilter中定義了多個action,intent必須滿足其中一個。
2)category的匹配規則:
所有隱式啟動Activity都會自動在intent中加入了category值為:android.intent.category.DEFAULT。所以在IntentFilter中必須定義
由于所有的隱式啟動都會自動加入category,所以可以設置intent的category,也可以不設置。但是如果intent中設置了category,那么就必須跟IntentFilter中的有匹配。
3)data的匹配規則:
data匹配分兩部分,mimeType和URI。
mimeType指媒體類型,
而URI包含的字段有:scheme、host、port、path、pathPattern、pathPrefix
data中如果沒有設置scheme的值,默認是content或者file。
如果IntentFilter中設置了data,那么intent中就必須設置data并且跟其中某一個data。這里匹配成功是指過濾條件中的部分data出現在Intent中的data。