Activity生命週期經典圖解:
按鍵對生命週期的影響:
BACK鍵:
當我們按BACK鍵時,我們這個應用程式將結束,這時候我們將先後調用onPause()->onStop()->onDestory()三個方法。
再次啟動App時,會執行onCreate()->onStart()->onResume()
HOME鍵:
當我們開啟應用程式時,比如瀏覽器,我正在瀏覽NBA新聞,看到一半時,我突然想聽歌,這時候我們會選擇按HOME鍵,然後去開啟音樂應用程式,而當我們按HOME的時候,Activity先後執行了onPause()->onStop()這兩個方法,這時候應用程式並沒有銷毀。
而當我們從案頭再次啟動應用程式時,則先後分別執行了onRestart()->onStart()->onResume()三個方法。
一般Activity切換正常生命週期(這裡的一般是指啟動模式為standard,切換activity時沒有加flag標誌):
ActivityA啟動ActivityB:
ActivityA 的生命週期onPause()->onStop(),
ActivityB的生命週期onCreate()->onStart()->onResume()。
ActivityB執行finish返回ActivityA:
ActivityB的生命週期onPause()->onStop()->onDestory()
ActivityA的生命週期了onRestart()->onStart()->onResume()
注意:當ActivityB定義為Dialog樣式時,ActivityA的生命週期是不一樣的,
我們給ActivityB加上theme
<style name="MyDialogStyle"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowFrame">@null</item> <item name="android:windowNoTitle">true</item> <item name="android:windowIsFloating">true</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowContentOverlay">@null</item> <item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item> <item name="android:backgroundDimEnabled">true</item> </style>
這個時候,ActivityA啟動ActivityB,B沒有完全遮擋A,ActivityB的生命週期跟剛才一樣,但是ActivityA並沒有執行onStop()
還有一點需要特別注意,Activity中直接彈dialog,Acitivity的生命週期是不會變化的。網上有些說法是會執行onPause(),其實並沒有執行!
另外還有幾個跟生命週期相關的方法
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); }
當應用運行起來後就會開啟一條線程,線程中會運行一個任務棧,當Activity執行個體建立後就會放入任務棧中。Activity啟動模式的設定在AndroidManifest.xml檔案中,通過配置Activity的屬性android:launchMode=""設定。
1. Standard模式(預設)
我們平時直接建立的Activity都是這種模式的Activity,這種模式的Activity的特點是:只要你建立了Activity執行個體,一旦啟用該Activity,則會向任務棧中加入新建立的執行個體,退出Activity則會在任務棧中銷毀該執行個體。
standard模式是所啟動的Activity都是在同一個task容器棧下,不會重新建立新的task容器棧。先壓入棧的Activity執行個體按順序入棧底,後入棧在棧頂,處於棧的頂部Activity執行個體處於活動狀態,其他處於非使用中。按物理返回鍵,退出當前所處活動狀態Activity視窗,這樣就會從task容器棧中彈出,顯示在手機主畫面上,從而,有非使用中轉換成活動的狀態。其次,standard容器棧可能會存在著相同的Activity執行個體,只有沒調用一次startActivity方法,就會建立目標Activity執行個體對象壓入task容器棧。
如果Activity啟動順序為A->B->B->A->D,棧中的Acitivy為ABBAD(最先建立的A位於棧底,最後建立的D位於棧頂)
2. SingleTop模式
這種模式會考慮當前要啟用的Activity執行個體在任務棧中是否正處於棧頂,如果處於棧頂則無需重新建立新的執行個體,會重用已存在的執行個體,否則會在任務棧中建立新的執行個體。
SingleTop有個不錯的用法是防止多次點擊建立多個Activity,無論start幾次,SingleTop模式能保證棧頂只有一個執行個體。 如果Activity啟動順序為A->B->B->A->D,棧中的Acitivy為ABAD(當B位於棧頂時,再次啟動B的時候,B不會重新建立)
3. SingleTask模式
如果任務棧中存在該模式的Activity執行個體,則把棧中該執行個體以上的Activity執行個體全部移除,調用該執行個體的newInstance()方法重用該Activity,使該執行個體處於棧頂位置,否則就重新建立一個新的Activity執行個體。
singletask模式,特別需要注意了。啟動的目標Activity執行個體如果已經存在task容器棧中,不管當前執行個體處於棧的任何位置,是棧頂也好,棧底也好,還是處於棧中間,只要目標Activity執行個體處於task容器棧中,都可以重用該Activity執行個體對象,然後,把處於該Activity執行個體對象上面全部Activity執行個體清除掉,並且,task容器棧中永遠只有唯一執行個體對象,不會存在兩個相同的執行個體對象。所以,如果你想你的應用不管怎麼啟動目標Activity,都只有唯一一個執行個體對象,就使用這種啟動模式。 如果Activity啟動順序為A->B->B->A->D,棧中的Acitivy為AD(當A再次被啟動時,A會被移到棧頂,位於A上面的Acitivity全部會出棧)
4. SingleInstance模式
當該模式Activity執行個體在任務棧中建立後,只要該執行個體還在任務棧中,即只要啟用的是該類型的Activity,都會通過調用執行個體的newInstance()方法重用該Activity,此時使用的都是同一個Activity執行個體,它都會處於任務棧的棧頂。此模式一般用於載入較慢的,比較耗效能且不需要每次都重新建立的Activity。
singleInstance啟動模式,簡單說就是可以共用某個Activity。比如,應用1的任務容器棧中建立了MainActivity執行個體,應用2也要啟用MainActivity,則不需要建立MainActivity執行個體,直接可以公用MainActivity執行個體。 尤其值得注意:應用1啟動MainActivity,按home鍵;開啟應用2啟動應用1的MainActivity執行個體。在按home鍵,開啟應用1,這時候應用1的介面是應該是處於MainActivity介面執行個體。 SingleInstance的一個任務棧中只有一個Activity,並保證不再有其他Activity執行個體進入。
特別需要注意的生命週期onNewIntent
當一個Activity被start,而不需要重新建立時,就會執行onNewIntent生命週期。如果一個Activity的啟動模式是SingleTask,我們可以在onNewIntent中執行一些重新整理操作等。
我們一般會把MainAcitivy設定為SingleTask,除了保證MainActivity的唯一,還可以利用singleTask的特性做一些清理工作。自動管理棧,銷毀無用的Acitivity.
Intent Flags
Flags: 表示Intent的標誌位,常用於Activity的情境中,它和Activity的啟動模式有著密切的聯絡。
下面列舉的是和本文主題相關的Flags屬性:
Intent.FLAG_ACTIVITY_NEW_TASK (預設)
預設的跳轉類型,它會重新建立一個新的Activity,不過與這種情況,比如說Task1中有A,B,C三個Activity,此時在C中啟動D的話,如果在AndroidManifest.xml檔案中給D添加了Affinity的值和Task中的不一樣的話,則會在新標記的Affinity所存在的Task中壓入這個Activity。如果是預設的或者指定的Affinity和Task一樣的話,就和標準模式一樣了啟動一個新的Activity.
FLAG_ACTIVITY_SINGLE_TOP這個FLAG就相當於啟動模式中的singletop,例如:原來棧中結構是A B C D,在D中啟動D,棧中的情況還是A,B,C,D。
FLAG_ACTIVITY_CLEAR_TOP這個FLAG就相當於啟動模式中的SingleTask,這種FLAG啟動的Activity會把要啟動的Activity之上的Activity全部彈出棧空間。例如:原來棧中的結構是A B C D ,從D中跳轉到B,棧中的結構就變為了A B了。
FLAG_ACTIVITY_BROUGHT_TO_FRONT
這個網上很多人是這樣寫的。如果activity在task存在,拿到最頂端,不會啟動新的Activity。這個有可能會誤導大家! 他這個FLAG其實是這個意思!比如說我現在有A,在A中啟動B,此時在A中Intent中加上這個標記。此時B就是以FLAG_ACTIVITY_BROUGHT_TO_FRONT方式啟動,此時在B中再啟動C,D(正常啟動C,D),如果這個時候在D中再啟動B,這個時候最後的棧的情況是 A,C,D,B。如果在A,B,C,D正常啟動的話,不管B有沒有用FLAG_ACTIVITY_BROUGHT_TO_FRONT啟動,此時在D中啟動B的話,還是會變成A,C,D,B的。
FLAG_ACTIVITY_NO_USER_ACTION
onUserLeaveHint()作為activity周期的一部分,它在activity因為使用者要跳轉到別的activity而要退到background時使用。比如,在使用者按下Home鍵,它將被調用。比如有電話進來(不屬於使用者的選擇),它就不會被調用。
那麼系統如何區分讓當前activity退到background時使用是使用者的選擇?
它是根據促使當前activity退到background的那個新啟動的Activity的Intent裡是否有FLAG_ACTIVITY_NO_USER_ACTION來確定的。
注意:調用finish()使該activity銷毀時不會調用該函數
FLAG_ACTIVITY_NO_HISTORY
意思就是說用這個FLAG啟動的Activity,一旦退出,它不會存在於棧中,比如原來是A,B,C這個時候再C中以這個FLAG啟動D的,D再啟動E,這個時候棧中情況為A,B,C,E。
Activity相關屬性taskAffinity
Activity 中的 android:taskAffinity 這個屬性介紹:
Activity為Task擁有的一個affinity。擁有相同的affinity的Activity理論上屬於相同的Task(在使用者的角度是相同的“應用程式”)。Task的affinity是由它的根Activity決定的。
affinity決定兩件事情——Activity重新宿主的Task(參考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK標誌啟動的Activity宿主的Task。
預設情況,一個應用程式中的所有Activity都擁有相同的affinity。捏可以設定這個特性來重組它們,甚至可以把不同應用程式中定義的Activity放置到相同的Task中。為了明確Activity不宿主特定的Task,設定該特性為空白的字串。
如果這個特性沒有設定,Activity將從應用程式的設定那裡繼承下來(參考<application>元素的taskAffinity特性)。應用程式預設的affinity的名字是<manifest>元素中設定的package名。