Activity的生命周期和启动模式
Activity作为四大组件之首,是使用最为频繁的一种组件。正常情况下,除了Window、Dialog和Toast,我们能见到的界面的确只有Activity。下面主要介绍生命周期和启动模式以及IntentFilter的匹配规则分析。
1. Activity的生命周期全面分析
Activity的生命周期分为两种,一种是典型情况下的生命周期,另一种是异常情况下的生命周期。典型情况下的生命周期是指在有用户参与的情况下,Activity所经过的生命周期的改变;异常情况下的生命周期是指Activity被系统回收或者由于当前设备的Configuration发生改变从而导致Activity被销毁重建。
1.1 典型情况下的生命周期分析
在正常情况下,Activity会经历如下生命周期。
- onCreate:表示Activity正在被创建,这是生命周期的第一个方法。我们常在这个方法里做一些初始化工作,比如调用setContentView()去加载界面布局资源、初始化Activity所需要的数据。
- onRestart:表示Activity正在重新启动。一般情况下,当当前Activity从不可见重新变成可见状态时,onRestart就会被调用。
- onStart:表示Activity正在被启动,此时Activity已经可见但是没有出现在前台,还无法与用户交互。
- onResume:表示Activity已经可见,并且出现在前台开始活动。
- onPause:表示Activity正在停止,正常情况下,紧接着onStop就会被调用。此时可以做一些存储数据、停止动画等工作,但是注意不能太耗时。因为这会影响到新Activity的显示,onPause必须先执行完,新Activity的onResume才会执行。
- onStop:表示Activity即将停止,可以做一些回收工作。
- onDestroy:表示Activity即将被销毁。这是生命周期最后一个回调,我们可以做一些回收工作和最终的资源释放。
我们都知道onPause和onStop都不能执行耗时的操作,尤其是onPause,这也意味着,我们应当尽量在onStop中做操作,从而使得新Activity尽快显示出来并切换到前台。
一些具体情况说明:
- Activity第一次启动:onCreate --》 onStart --》 onResume
- 打开新的Activity或者切换到桌面:onPause --》 onStop
- 再次回到原Activity:onRestart --》 onStart --》 onResume
- 按下back键回退:onPause --》 onStop --》 onDestroy
- onCreate和onDestroy是一对,表示Activity的创建和销毁,只能有一次调用。onStart和onStop是一对,可被多次调用。onResume和onPause是一对,可多次被调用。
【一些问题】
Q1:onStart和onResume、onPause和onStop从描述上看差不多,对我们来说有什么实质的不同呢?
A:这两个配对的回调分别表示不同的意义。onStart和onStop是从Activity是否可见的角度来回调的,而onResume和onPause是从Activity是否位于前台这个角度来回调的,除了这种区别,在实际使用中没有其他明显的区别。
Q2:假设当前Activity为A,如果这时用户打开一个新Activity B,那么B的onResume和A的onPause哪个先执行呢?
A:A的onPause先执行,B的onResume后执行。Activity启动过程简单理解,启动Activity的请求会由Instrumentation来处理,然后它通过Binder向AMS发请求,AMS内部维护着一个ActivityStack并负责栈内的Activity的状态同步,AMS通过ActivityThread去同步Activity的状态从而完成生命周期方法的调用。
1.2 异常情况下的生命周期分析
1. 资源相关的系统配置发生改变导致Activity被杀死并重新创建
比如说当前Activity处于竖屏状态,如果突然旋转屏幕,由于系统配置发生了变化,在默认情况下,Activity就会被销毁并且重新创建,当然我们也可以阻止系统重新创建我们的Activity。
当Activity被重新创建后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数同时传递给onRestoreInstanceState和onCreate方法。从时序上来看,onRestoreInstanceState的调用时机在onStart之后。
通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。
Q3:onSaveInstanceState 和 onRestoreInstanceState
A:onSaveInstanceState()的调用遵循一个重要原则,即当系统存在“未经你许可”时销毁了我们的activity的可能时,则onSaveInstanceState()会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。如果调用,调用将发生在onPause()或onStop()方法之前。onRestoreInstanceState()被调用的前提是,activity A“确实”被系统销毁了并重建,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,onRestoreInstanceState()在onStart() 和 onResume()之间调用。
委托思想:上层委托下层、父容器委托子元素去处理一件事情。比如View的绘制过程、事件分发等都是采用类似的思想。
2. 资源内存不足导致低优先级的Activity被杀死
Activity按照优先级从高到低,可以分为三种:
分类 | 优先级描述 |
---|---|
前台Activity | 正在和用户交互的Activity,优先级最高 |
可见但非前台Activity | 对话框导致Activity可见但是位于后台,无法与用户直接交互 |
后台Activity | 已经被暂停的Activity,比如执行了onStop,优先级最低 |
如果一个进程中没有四大组件在执行,那么这个进程将很快被系统杀死。因此,一些后台工作不适合脱离四大组件而独自运行在后台中,这样进程很容易被杀死。比较好的方法是将后台工作放入Service中从而保证进程有一定的优先级,这样就不会轻易被系统杀死。
如果我们不想Activity在某些时候被重新创建,可以给configChanges属性添加值。
上面表格中的项目很多,但是我们常用的只有locale、orientation和keyboardHidden三个选项。
2. Activity的启动模式
2.1 Activity的LaunchMode
目前Android有四种启动模式:standard、singleTop、singleTask和singleInstance。
模式 | 描述 |
---|---|
standard | 标准模式,也是系统的默认模式。每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否已经存在 |
singleTop | 栈顶复用模式。如果新Activity已经位于栈顶,那么Activity不会被重新创建,同时onNewIntent方法会被回调 |
singleTask | 栈内复用模式。一种单实例模式,只要Activity在栈中存在,那么多次启动此Activity都不会重新创建实例,也会回调onNewIntent |
singleInstance | 单实例模式。它除了具有singleTask的所有特性外,此种模式的Activity只能单独的位于一个任务栈中 |
TaskAffinity
TaskAffinity可以翻译为任务相关性。这个参数标识了一个Activity所需要的任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名。taskAffinity这个属性的值为字符串,且中间必须含有包名分隔符"."。TaskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配合使用。任务栈也分为前台任务栈和后台任务栈,后台任务栈中的Activity位于暂停状态,用户可以通过切换将后台任务栈再次调到前台。
Activity指定启动模式的方式:
- 通过AndroidManifest为Activity指定启动模式
- 通过在Intent中设置标志位来为Activity指定启动模式
2.2 Activity的Flags
大部分情况下,我们不需要为Activity指定标记位,这里主要介绍一些常用的标记位。
标记位 | 作用 |
---|---|
FLAG_ACTIVITY_NEW_TASK | 为Activity指定“singleTask”启动模式 |
FLAG_ACTIVITY_SINGLE_TOP | 为Activity指定“singleTop”启动模式 |
FLAG_ACTIVITY_CLEAR_TOP | 清除标记位Activity顶上的Activity |
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | 该Activity不会出现在历史Activity列表中 |
3. IntentFilter的匹配规则
我们知道,启动Activity分为两种,显示调用和隐式调用。隐式调用需要Intent能够匹配目标组件的IntentFilter中所设置的过滤信息,如果不匹配将无法启动目标Activity。IntentFilter中的过滤信息有action、category、data。
- action的匹配规则
action是一个字符串,action的匹配要求规则是Intent中的action存在且必须和过滤规则中的其中一个action相同。另外,action区分大小写,大小写不同字符串相同的action会匹配失败。 - category的匹配规则
category是一个字符串,category要求Intent可以没有category,但是如果你一旦有category,不管有几个,每个都要能够和过滤规则中的任何一个category相同。 - data的匹配规则
data由两部分组成,mimeType和URI。mimeType指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式。URI中包含的数据就比较多了,结构长这样:
再举几个实际点的例子:
URI中每个数据的含义:
- Scheme:URI的模式,比如http、file、content等。如果URI没有指定scheme,那么整个URI的其他参数无效,这也意味着URI无效。
- Host:URI的主机名,比如www.baidu.com等。如果host未指定,那么整个URI中的其他参数无效,这也意味着URI无效。
- Port:URI中的端口号,比如80,仅当URI中指定了scheme和host参数的时候port参数才有意义。
- Path、pathPattern、pathPrefix:这三个参数表述路径信息。
data要求Intent中必须含有data数据,并且data数据能够完全匹配过滤规则中的某一个data。
在action和category中,有一类action和category比较重要,它们是:
这两者共同作用是用来表明这是一个入口Activity并且会出现在系统的应用列表中,少了任何一个都没有实际意义,也无法出现在系统的应用列表中,也就会二者缺一不可。