Activity是Android负责与用户交互的实体。一个应用一般由多个Activity组成;一个Activity不仅可以打开应用内的Activity,也可以打开其他应用内的Activity,比如通过一个名为android.intent.action.VIEW
的Intent事件可以打开浏览器;通过android.intent.action.DIAL
可以向CALL发送拨号请求。一系列的Activity组合在一起(完成某个特定的“任务“)构成了一个任务(一个后进先出的队列,被称为back stack),而多个任务又构成了一个任务栈(后台可以有多个任务栈,但系统如需恢复内存,则会杀死部分应用,清空部分任务,以释放内存,因此长时间后应用状态会丢失)。以下图为例,图中的back stack一开始只有一个Activity1;接着在Activity1中启动Activity2,此时Activity2位于队列的顶部,而Activity1位于栈底(Activity1处于Stop状态),而Activity2又启动一个Activity3,同样,Activity3压入栈顶。如果此时通过回退键(Back)用户离开Activity3,则Activity3从栈中清除,被系统销毁,而Activity2则恢复到可见状态(resume)。
总的说来,Activity与任务栈之间的关系有如下几种情况:
- 当Activity A启动ActivityB时,A处于停止(stop)状态,但是系统会保留其状态(如当前滑动位置以及输入的文本等);如果在B中用户按下回退键,此时A恢复原来的状态,而B被销毁;
- 当用户按下HOME键离开一个任务时,当前Activity会被停止,相应的任务进入后台,系统会保留任务中所有Activity的状态。如果用户稍后通过启动界面(Launcher)的图标启动该任务,则任务恢复到前台运行,使任务顶部的Activity处于可见状态(resume);
- 用户按下回退(back)键时,当前Activity从栈中弹出,销毁(系统不再保留次Activity状态),此时之前的Activity恢复运行;
- 一个Activity可被实例化多次,该实例可以来自不同的任务(back stack);
那么,Android是如何来管理这些任务的了?这里有这么几个问题:
- 刚讲到,一个Activity可能在一个任务中被实例化多次,那么如何避免这样多次实例化的开销了?
- 怎么来控制一个Activity所属的任务(back stack)?
- 怎么启动一个新的任务?
任务管理
Android通过启动模式(Launch Mode)来控制Activity的启动方式,也可以通过控制Activity中的某些属性来控制Activity在任务中的行为。启动模式主要有两种控制方式,一个是在manifest文件的<activity>
标签来指定,一种是通过Intent中的标志位。接下来就来看下这两种控制方式。
使用manifest文件
<activity>
中有一个launchMode
的属性来指定Activity启动的模式:
- “standard”: 默认的启动模式,系统创建一个新的Activity实例,并放入启动该Activity的任务中。在这种模式下,一个Activity可能被实例化多次,每个实例可能属于不同的任务,而且有可能一个任务里存在多个实例;
- “singleTop“: 如果当前任务的顶部已存在该Activity实例,则将intent通过
onNewIntent
方法传给该Activity,不会再创建一个新的Activity实例; - “singleTask”: 创建一个新的任务,并将该Activity放入任务(back stack)的底部;但是如果其他任务栈中已存在该Activity实例,则不会创建新的任务,而是将该Activity实例所在任务放置到前台运行,intent则通过
onNewIntent()
方法传给该Activity。该启动模式确保任务栈中始终只有一个Activity实例存在; - “singleInstance”: 与
singleTask
相同,唯一的区别在于该模式下系统不会将其他Activity放入到该Activity所在的任务中,并且任何通过该Activity启动的Activity都会在另一个任务当中,就是说,当前Activity始终是其任务中唯一的一个成员;
使用Intent标志位
当通过startActivity()
来启动Activity时,可以通过Intent的标志位来控制Activity的启动方式:
- FLAG_ACTIVITY_NEW_TASK: 行为与
launchMode
中的singleTask
一样; - FLAG_ACTIVITY_SINGLE_TOP: 行为与
launchMode
中的singleTop
一样; - FLAG_ACTIVITY_CLEAR_TOP: 这个标志在
launchMode
中没有对应的值。如果当前任务中已存在该Activity的实例,则将所有在该Activity之上的其他Activity都销毁,该Activity置于任务的栈顶,并通过onNewIntent()
方法将intent传给该实例。
处理亲和标志
affinity(亲和性)表示一个activity属于哪个任务(back stack)。Android默认在同一个进程中,所有的Activity相互之间都有同样的affinity,就是说在同一个任务中。但是,通过taskAffinity
属性值我们可以修改一个Activity属于的任务,这样不同应用之前的Activity可以共有一个affinity,而同一个应用之间的Activity也可以有不同的affinity:
- 对于Activity来说,可通过标签
<activity>
中的属性taskAffinity
来指定affinity; - 对于一个应用来说,
<application>
中的taskAffinity
属性默认为<manifest>
中指定的程序包名;
一般有两种情况会影响到Activity的affinity:
- 启动Activity时,设置了标志位
FLAG_ACTIVITY_NEW_TASK
,此时如果系统没有包含该Activity实例的任务,则启动新的任务;否则该Activity从已有任务中启动; <activity>
中的属性值allowTaskReparenting
设为true,在这种情况下,如果Activity所属affinity的任务到前台运行,则该Activity可能从启动它的任务(back stack)中移动到它所属affinity的任务当中。
清除任务
如果用户长时间离开一个任务,系统会清理除根Activity以外的所有Activity。当用户返回应用后,仅有根Activity被恢复。但可以通过如下几个<activity>
的属性值来更改系统的这一行为:
alwaysRetainTaskState
:如果在一个任务中的根Activity中的将其设为true,则不会任务中的任何Activity,即使长时间后,依然保留所有Activity在任务中;clearTaskOnLaunch
:其作用恰好与alwaysRetainTaskState
相反;如果在根Activity中将该属性值设为true,则只要用户离开任务,则会将除根Activity之外的Activity都清除,就是说,每次用户返回任务时,都是一个最开始初始化的状态;finishOnTaskLaunch
:与clearTaskOnLaunch
作用类似,但其只局限于单个Activity,而不是整个任务(back stack),它可以使任何一个Activity被清除,包括根Activity。该属性设为true时,应用只是为当前会话而保留在任务中,一旦用户离开,稍后返回任务,则不能再看到该Activity。