深入理解Activity生命周期、启动模式及IntentFilter

Activity作为Android四大组件中最常用的组件,对其进行深入的学习是一个初级Android开发人员成长路上的必修课。
本篇文章主要针对Activity的生命周期异常状态恢复启动模式以及Intent隐式启动做相应的深入学习,挖掘Activity的各项进阶知识。

1.Activity的生命周期

先上一张官方的Activity生命周期图

Activity生命周期

接下来我们对Activity生命周期中的各状态进行逐一分析:
(1)onCreate:该方法只在Activity第一次被创建的时候调用。我们常常在其中进行一些初始化的操作,如setContentView、绑定事件、初始化各类数据等。
(2)onStart:该方法表示Activity正在启动。一般发生在Activity由不可见转变为可见时被调用。(此时的Activity已经可见了,但还没出现在前台,无法进行交互)
(3)onResume:该方法表示Activity不仅已经可见,而且此刻位于前台,可以与用户进行交互。此时该Activity一定位于当前返回栈的栈顶,且处于运行状态。
(4)onPause:该方法表示Activity正在停止,一般情况下onStop方法会紧接着被调用。通常我们会在这个方法中去做一些储存数据、释放资源的操作。此时该Activity将不再位于前台。注意的是,该方法中切不可执行耗时操作,否则会影响到新Activity的显示。
(5)onStop:该方法在活动完全不可见时被调用,可以在该方法内执行一些稍微重量级的回收工作,同样不可太耗时。
(6)onDestroy:该方法表示Activity即将被销毁,这是Activity生命周期中的最后一步,应该在该方法内执行最终的回收工作和资源释放。
(7)onRestart:该方法在Activity由停止状态转为运行状态的过程中被调用,一般发生在返回到上一个Activity这种情况下。

上述7个生命周期中的回调方法中,除了OnRestart方法,其他方法都是两两相对的,我们可以从下面几种角度对它们进行加深理解:
Activity创建和销毁的角度来看,分别对应着OnCreate和OnDestroy;
Activity是否可见的角度来看,分别对应着OnStart和OnStop;
Activity是否位于前台的角度来看,分别对应着OnResume和OnPause。

下面列举几种情况下Activity的执行流程:
①Activity启动:onCreate->onStart->onResume
②打开新Activity或切换到桌面:onPause->onStop(特殊情况,当新Activity采用透明主题时,当前Activity不会回调onStop)
③回到原Activity:onRestart->onStart->onResume
④按back键回退:onPause->onStop->onDestroy

特别说明:

当启动一个新Activity时,一定是先执行当前Activity的onPause方法后,才会启动新的Activity,最后执行当前Activity的onStop方法。(所以onPause方法中一定不能执行耗时操作)

2.Activity异常状态下的生命周期

当资源相关的系统配置发生改变或是系统内存不足时,Activity可能会被杀死。由于Activity是被异常销毁掉的,所以用户的一些临时数据需要进行保存,以使该Activity重新创建时能复现销毁时的内容,如EditText中的内容等。
由系统内存不足所涉及到的进程优先级的问题,参考Android的进程优先级
当系统配置发生改变后,Activity会被销毁,其onPause、onStop、onDestroy均会被调用,同时由于Activity是在异常情况下被终止的,所以系统会调用onSaveInstanceState方法来保存当前Activity的状态。该方法的调用时机在onStop之前,但和onPause没有绝对的时序关系。
当Activity被重新创建之后,系统会回调onRestoreInstanceState方法,并把Activity销毁时在onSaveInstanceState方法中所保存的Bundle作为参数传递到onCreate和onRestoreInstanceState方法中。从时序上来说,onRestoreInstanceState调用时机在onStart之后
onRestoreInstanceState和onCreate虽然都能获得异常时传入Bundle参数,但是二者还是有一定的区别:onRestoreInstanceState方法一旦被调用,其Bundle参数一定是有值的;而onCreate方法在正常启动的时候,其Bundle参数为null,所以必须进行额外的判断才行。官方推荐使用onRestoreInstanceState方法进行数据恢复。

多次试验后的总结下,onSaveInstanceState(Bundle outState)会在以下情况被调用:
1、当用户按下HOME键时。
2、从最近应用中选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从当前activity启动一个新的activity时。
5、屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)。

在前4种情况下,当前activity的生命周期为:
onPause -> onSaveInstanceState -> onStop -> onRestart -> onStart -> onResume
当该activity重新位于前台时,并不会执行onRestoreInstanceState方法。

第五种情况下,当前activity的生命周期为:
onPause -> onSaveInstanceState -> onStop -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume

当然,当系统配置发生变化时,Activity也能不被重建。通过给Activity指定configChanges属性可以实现这一目标,如不想让Activity在屏幕旋转时重建,就可以给configChanges属性添加orientation这个值。
android:configChanges="orientation"
通常我们用的只有local(系统语言)、orientation(屏幕方向)和keyboardHidden(调出键盘)这三个选项。

3.Activity的启动模式

系统会将启动的Activity放入ActivityTask中,该任务栈遵循后进先出的规则。当然,Android中还能通过指定lauchmode的方式来个性化activity在栈中的启动模式。

Activity有四种启动模式standard、singleTop、singleTask和singleInstance。

standard:activity的默认启动模式。每次启动一个Activity的时候都会重新创建一个新的实例压入栈顶,不管当前栈中是否已存在该Activity。
singleTop栈顶复用模式。如果当前任务栈的栈顶是该Activity,那么此Activity不会被重新创建,同时会调用栈顶Activity的onNewIntent方法,通过此方法获取当前的请求信息。此时该Activity的onCreate、onStart不会被系统调用,仅执行onPause -> onNewIntent -> onResume;如果新Activity的实例在任务栈中已存在但并未位于栈顶,那么新Activity则依旧会重新创建。
singleTask栈内复用模式。这是一种单实例模式,该模式下,只要当前任务栈内存在即将要启动的Activity(不管在栈内何处),那么当前栈中该Activity上面的所有Activity都将出栈,并且该Activity将位于栈顶。和singleTop一样,singleTask也会调用该Activity的onNewIntent方法。(注意:singleTask模式下可以指定该Activity启动所需要的任务栈,若指定任务栈不存在,则会新建对应的任务栈后再把该Activity新建的实例放进去)。
singleInstance单实例模式。指定为singleInstance的Activity会启用一个新的任务栈来管理自身。只要该Activity未销毁,由于栈内复用的特性,后续的请求不会再创建新的任务栈,会一直复用处于该任务栈内的Activity。

特殊情况:TaskAffinity

上面提到在singleTask模式下,Activity需要选择自己所指定的任务栈,那么这个指定的任务栈又是在哪实现的呢?
答案就在TaskAffinity这个属性上。该参数能标识Activity所需任务栈的名字,默认情况下所有Acitivity的任务栈都为应用的包名。TaskAffinity属性的值为字符串,且中间必须包含包名分隔符“.”。TaskAffinity属性主要和singleTask模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。
当TaskAffinity和singleTask模式配对使用时,待启动的Activity会运行在TaskAffinity指定的任务栈中。
当TaskAffinity和allowTaskReparenting配对使用时,情况较为复杂。例如有两个应用A和B,A启动了B中的Activity并且该Activity设置的allowTaskReparenting属性为true,那么当用户按home建回到桌面后再点击应用B图标,则不是启动了B的Launch Activity,而是重新显示的之前被A所启动的那个Activity

除了通过launchmode来指定Activity的启动模式,还能通过Intent来动态指定Activity的启动模式。其中,Intent动态指定的优先级高于在xml中的静态注册方式。
动态指定主要是通过往Intent中添加Flag实现,如
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

以下列举几个常见的标记位,稍作理解即可。
FLAG_ACTIVITY_NEW_TASK:和在xml中指定singleTask模式相同
FLAG_ACTIVITY_SINGLE_TOP:和在xml中指定singleTop模式相同
FLAG_ACTIVITY_CLEAR_TOP:启动该Activity时,与其同一任务栈中所有位于它上面的Activity都要出栈。如果被启动的Activity采用standard模式,那么连同它之上的Activity都要出栈,系统再创建新的Activity实例放入栈顶。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:具有此标记的Activity不会出现在历史Activity列表中。等同于在xml中指定Activity属性android:excludeFromRencents="true"

4.IntentFilter

IntentFilter主要作用于Intent的隐式调用中。IntentFilter为Intent的隐式调用设置了过滤规则,如果不匹配将无法成功启动目标Activity。IntentFilter中过滤的信息有actioncategorydata

一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter就可以成功启动对应的Activity。

action:action的匹配要求是Intent中的action存在且必须和过滤规则中的其中一个action相同,这里的相同指的是Intent中的action必须能够和过滤规则中的action的字符串的值完全一样。action区分大小写。

categoryIntent中如果含有category,那么所有出现的category都必须和过滤规则中的其中一个category相同。(为了使Activity能接受到隐式调用,必须在intent-filter中指定"android.intent.category.DEFAULT"这个category,以免在Intent未指定category时添加的默认值无法匹配)

data:data和action的匹配原则类似。由于data的结构过于复杂且平时较少涉及到,故此处不展开学习。

在action和category中,有一类action和category比较特别:

<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

它们的共同作用标明了这是一个入口Activity,二者缺一不可。


参考文献:

《第一行代码——Android(第2版)》
《Android开发艺术探索》
《深入理解Android内核设计思想》



若您觉得本文章对您有用,请您为我点上一颗小心心以表支持。感谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容