Android Activity生命周期和啟動模式你不知道的那些事

此文是根據官方文檔、圖書資料及demo測試得出的結論,包括Activity多個頁面間跳轉的生命周期,各個啟動模式的不同,flag的常見用法及對activity任務棧的影響。

Activity生命周期需要注意的地方

  • 當Activity長時間在后臺運行時,可能會被殺死,再次啟動時Activity會被重建,此情況和橫豎屏切換場景類似,開發者應該在開發環境下打開橫豎屏切換測試,防止異常發生;
  • 如果新的Activity是透明主題,則當前Activity不會執行onStop;
  • onSaveInstanceState在onStop之前執行,onRestoreInstanceState在onStart之后執行;
  • 為防止Activity橫豎屏切換時Activity重啟,可添加 android:configChanges="orientation|screenSize"
  • onStart和onStop,判斷Activity是否可見,onResume和onPause,判斷Activity是否在前臺;
  • A啟動B,A、B生命周期執行順序:A會先進入后臺,B創建進入前臺,A不可見;
A(onPause) --> B(onCreate) --> B(onStart) --> B(onResume) --> A(onSaveInstanceState) ->A(onStop) 

Activity啟動模式

Standard:標準模式

  • 每次都會新創建Activity,并添加到對應的任務棧中;

SingleTop:棧頂復用

  • 如果要啟動的Activity已經在棧頂,該Activity不會被創建;

    * A --> B(SingleTop)
    * 再次啟動B后生命周期:
    onPause --> onNewIntent --> onResume
    
  • 如果要啟動的Activity不在棧頂,跟標準模式完全一致;

    * A --> B(SingleTop) --> C
    * 再次啟動B后棧:
    A --> B(SingleTop) --> C --> B(SingleTop)
    

SingleTask:棧內復用模式

  • 特別注意,配置此模式的Activity并不會位于一個新的任務棧中,只是如果要啟動的Activity已經在棧中,會clearTop,讓自己置為棧頂;

    * A --> B(SingleTask) --> C
    TaskRecord{ed93b47 #259 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=4}
        Run #3: ActivityRecord{c1f4817 u0 test.gcoder.io.testactivity/.CActivity, isShadow:false t259}
        Run #2: ActivityRecord{59e1a72 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t259}
        Run #1: ActivityRecord{d7ae33c u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t259}
        
    * 再次啟動B后任務棧:
    A --> B(SingleTask)
    此時C會被銷毀
    此時B的生命周期:onPause --> onNewIntent --> onResume
        
    TaskRecord{ed93b47 #259 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=3}
        Run #2: ActivityRecord{59e1a72 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t259}
        Run #1: ActivityRecord{d7ae33c u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t259}
    

SingleInstance:單實例模式

  • 此模式的Activity只能位于一個獨立任務棧中;

    * A --> B(SingleInstance) --> C
    此時,A、C位于同一個任務棧,B位于獨立的任務棧;
        
    TaskRecord{c37f244 #253 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=3}
        Run #3: ActivityRecord{8d06468 u0 test.gcoder.io.testactivity/.CActivity, isShadow:false t253}
    TaskRecord{10b2e56 #257 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=1}
        Run #2: ActivityRecord{eeb0317 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t257}
    TaskRecord{c37f244 #253 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=3}
        Run #1: ActivityRecord{e25c532 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t253}
        
    * 再次啟動B后棧:
    A --> C --> B(SingleInstance)
    此時,B調整為前臺任務棧,A、C的任務棧調整為后臺任務棧;
    此時,B的生命周期:onPause --> onNewIntent --> onResume
    
    TaskRecord{10b2e56 #257 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=1}
        Run #3: ActivityRecord{eeb0317 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t257}
    TaskRecord{c37f244 #253 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=3}
        Run #2: ActivityRecord{8d06468 u0 test.gcoder.io.testactivity/.CActivity, isShadow:false t253}
        Run #1: ActivityRecord{e25c532 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t253}
    

怎么確定Activity位于哪個任務棧

  • 除SingleInstance模式的activity外,其它所有模式的activity都會被添加到默認的任務棧中,默認任務棧taskAffinity為包名;

  • 可以通過指定taskAffinity讓activity運行在特定的任務棧中,必須配合FLAG_ACTIVITY_NEW_TASK才會生效;

  • 除SingleInstance模式的activity外,啟動普通的activity都被添加到啟動此activity所用的activity相同的任務棧中,除非同時指定taskAffinity和FLAG_ACTIVITY_NEW_TASK,會運行在與taskAffinity同名的任務棧中;

    * A --> B(taskAffinity=test.gcoder.io.newtask flag=FLAG_ACTIVITY_NEW_TASK)
    TaskRecord{99b9ba5 #269 A=test.gcoder.io.newtask, isShadow:false U=0 sz=1}
        Run #2: ActivityRecord{5ba0ca2 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t269}
    TaskRecord{ab040b2 #267 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=2}
        Run #1: ActivityRecord{1ddfc0 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t267}
        
    * A --> B(taskAffinity=test.gcoder.io.newtask flag=FLAG_ACTIVITY_NEW_TASK)--> C
    TaskRecord{99b9ba5 #269 A=test.gcoder.io.newtask, isShadow:false U=0 sz=2}
        Run #3: ActivityRecord{199ec49 u0 test.gcoder.io.testactivity/.CActivity, isShadow:false t269}
        Run #2: ActivityRecord{5ba0ca2 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t269}
    TaskRecord{ab040b2 #267 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=2}
        Run #1: ActivityRecord{1ddfc0 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t267}
    

Context對Activity任務棧的影響

  • 經過對比,使用Activity Context啟動與使用Application Context啟動Activity無任何區別,都是在一個任務棧中;

  • 使用Activity Context啟動

    * A --> B(FLAG_ACTIVITY_NEW_TASK)
    TaskRecord{6a79d7e #261 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=3}
        Run #2: ActivityRecord{8754172 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t261}
        Run #1: ActivityRecord{43b510 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t261}
            
    * A --> B(FLAG_ACTIVITY_NEW_TASK)-->C
    TaskRecord{6a79d7e #261 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=4}
        Run #3: ActivityRecord{4095453 u0 test.gcoder.io.testactivity/.CActivity, isShadow:false t261}
        Run #2: ActivityRecord{8754172 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t261}
        Run #1: ActivityRecord{43b510 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t261}   
    
  • 使用Application Context啟動

    * A --> B(FLAG_ACTIVITY_NEW_TASK)
    TaskRecord{eb6f24d #262 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=3}
        Run #2: ActivityRecord{215a79a u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t262}
        Run #1: ActivityRecord{7e22051 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t262}
          
    * A --> B(FLAG_ACTIVITY_NEW_TASK)--> C      
    TaskRecord{eb6f24d #262 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=4}
        Run #3: ActivityRecord{dac68e6 u0 test.gcoder.io.testactivity/.CActivity, isShadow:false t262}
        Run #2: ActivityRecord{215a79a u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t262}
        Run #1: ActivityRecord{7e22051 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t262}
        
    * 再次啟動B后:A --> B(FLAG_ACTIVITY_NEW_TASK) --> C --> B(FLAG_ACTIVITY_NEW_TASK)      
    TaskRecord{eb6f24d #262 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=5}
        Run #4: ActivityRecord{2ec8ea1 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t262}
        Run #3: ActivityRecord{c30b233 u0 test.gcoder.io.testactivity/.CActivity, isShadow:false t262}
        Run #2: ActivityRecord{e88653e u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t262}
        Run #1: ActivityRecord{177c4a8 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t262}
    

Flag對Activity任務棧的影響

FLAG_ACTIVITY_NEW_TASK

  • 只添加此Flag的Activity不會被添加新的任務棧中,此時跟SingleTask無任何關系,也不具備SingleTask同一個任務棧中唯一的特性及clearTop的特性;
  • 此Flag可被用于非Activity類型的Context啟動新Activity;
  • 此Flag與其它Flag混合使用會有不同的效果;

FLAG_ACTIVITY_MULTIPLE_TASK

  • 此Flag一般不單獨使用,需要配合FLAG_ACTIVITY_NEW_TASK使用;

FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_ACTIVITY_NEW_TASK

  • 單獨使用時,會檢索已經存在的任務棧,如果啟動的activity對應的任務棧存在就不會創建,不存在才會創建新的任務棧;

  • 同時使用時,會強制創建新的任務棧,并將啟動的activity放在新的任務棧中, 后續使用此activity啟動的標準模式的activity會運行在此棧中,而不是默認棧中;

    * A --> B(FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_ACTIVITY_NEW_TASK)
    TaskRecord{4144b1a0 #6 A test.gcoder.io.testactivity U 0}
        Run #3: ActivityRecord{413fd7a8 test.gcoder.io.testactivity/.BActivity}
    TaskRecord{41548560 #5 A test.gcoder.io.testactivity U 0}
        Run #2: ActivityRecord{41562330 test.gcoder.io.testactivity/.AActivity}
            
    * A --> B(FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_ACTIVITY_NEW_TASK) --> C
    TaskRecord{4144b1a0 #6 A test.gcoder.io.testactivity U 0}
        Run #4: ActivityRecord{41604278 test.gcoder.io.testactivity/.CActivity}
        Run #3: ActivityRecord{413fd7a8 test.gcoder.io.testactivity/.BActivity}
    TaskRecord{41548560 #5 A test.gcoder.io.testactivity U 0}
        Run #2: ActivityRecord{41562330 test.gcoder.io.testactivity/.AActivity}
       
    * A --> B(FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_ACTIVITY_NEW_TASK) --> C --> B(FLAG_ACTIVITY_MULTIPLE_TASK|FLAG_ACTIVITY_NEW_TASK)
    TaskRecord{41434a90 #7 A test.gcoder.io.testactivity U 0}
        Run #5: ActivityRecord{413a6710 test.gcoder.io.testactivity/.BActivity}
    TaskRecord{4144b1a0 #6 A test.gcoder.io.testactivity U 0}
        Run #4: ActivityRecord{41604278 test.gcoder.io.testactivity/.CActivity}
        Run #3: ActivityRecord{413fd7a8 test.gcoder.io.testactivity/.BActivity}
    TaskRecord{41548560 #5 A test.gcoder.io.testactivity U 0}
        Run #2: ActivityRecord{41562330 test.gcoder.io.testactivity/.AActivity} 
    

FLAG_ACTIVITY_SINGLE_TOP

  • 同SingleTop

FLAG_ACTIVITY_CLEAR_TOP

  • 此Flag單獨使用時,如果啟動的Activity存在對應的任務棧中,只是會清除任務棧中要啟動的Activity之上所有的Activity,要啟動的Activity會先銷毀后重新創建;

  • 此Flag單獨使用時,如果啟動的Activity不存在對應的任務棧中,則創建并添加到對應的任務棧中;

    * A --> B(FLAG_ACTIVITY_CLEAR_TOP) --> C
    TaskRecord{166703a #289 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=4}
        Run #3: ActivityRecord{374b0ff u0 test.gcoder.io.testactivity/.CActivity, isShadow:false t289}
        Run #2: ActivityRecord{15086 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t289}
        Run #1: ActivityRecord{da843b0 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t289}
            
    * 再次啟動B(FLAG_ACTIVITY_CLEAR_TOP)
    * 生命周期:C會銷毀,B會先銷毀后創建
    C(onPause)
    B(onDestroy)-->B(onCreate)-->B(onStart)-->B(onResume)
    C(onStop)-->C(onDestroy)
        
    TaskRecord{166703a #289 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=3}
        Run #2: ActivityRecord{6420128 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t289}
        Run #1: ActivityRecord{da843b0 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t289}
    

FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK

  • 如果要啟動的Activity在對應的任務棧中,則會銷毀任務棧頂部的所有 Activity,并通過 onNewIntent() 將此 Intent 傳遞給 Activity 已恢復的實例(現在位于頂部),而不是啟動該 Activity 的新實例;

  • 如果啟動的Activity沒有在對應的任務棧中,則創建新的任務棧,創建Activity并添加到新的任務棧中;

    * A --> B(FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_ACTIVITY_CLEAR_TOP)
    TaskRecord{413377c #286 A=test.gcoder.io.newtask, isShadow:false U=0 sz=1}
        Run #2: ActivityRecord{a58be9a u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t286}
    TaskRecord{cfe9805 #285 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=2}
        Run #1: ActivityRecord{b5a1c24 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t285}
        
    * A --> B(FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_ACTIVITY_CLEAR_TOP)--> C
    TaskRecord{413377c #286 A=test.gcoder.io.newtask, isShadow:false U=0 sz=2}
        Run #3: ActivityRecord{9f7c8df u0 test.gcoder.io.testactivity/.CActivity, isShadow:false t286}
        Run #2: ActivityRecord{a58be9a u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t286}
    TaskRecord{cfe9805 #285 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=2}
        Run #1: ActivityRecord{b5a1c24 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t285}
        
    * 再次啟動B(FLAG_ACTIVITY_MULTIPLE_TASK | FLAG_ACTIVITY_CLEAR_TOP)
    * 生命周期:C會銷毀,B會先銷毀后創建
    C(onPause)
    B(onDestroy)-->B(onCreate)-->B(onStart)-->B(onResume)
    C(onStop)-->C(onDestroy)
        
    TaskRecord{413377c #286 A=test.gcoder.io.newtask, isShadow:false U=0 sz=1}
        Run #2: ActivityRecord{ff16c57 u0 test.gcoder.io.testactivity/.BActivity, isShadow:false t286}
    TaskRecord{cfe9805 #285 A=test.gcoder.io.testactivity, isShadow:false U=0 sz=2}
        Run #1: ActivityRecord{b5a1c24 u0 test.gcoder.io.testactivity/.AActivity, isShadow:false t285} 
    

注:如果指定 Activity 的啟動模式為 "standard",則該 Activity 也會從堆棧中移除,并在其位置啟動一個新實例,以便處理傳入的 Intent。 這是因為當啟動模式為 "standard" 時,將始終為新 Intent 創建新實例。

參考資料:

本文作者:gcoder.io
本文鏈接:http://gcoder-io.github.io/2016/10/05/android-activity-lifecycle-launchmode/
版權聲明: 本博客所有文章均為原創,轉載請注明作者及出處

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容