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阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容