一个程序通常包含多个activity,每个activity都被设计来完成指定的动作,并且可以启动另外的activity。例如,一个邮件程序可以有一个显示邮件列表的activity,当用户选择一个邮件时,一个新的显示邮件内容的activity被启动。
当activity被停止时,系统可能会销毁它,当是系统仍然知道这个activity在回退堆栈中,这种情况下,系统就必须重新创建这个activity,为了避免用户状态丢失,我们应该实现OnSaveInstanceState()方法。详细用法看activity章节。
你可能希望在每次启动一个程序中的activity时都开始一个新的任务,而不是继续当前任务,或者你想在启动一个activity时调用已经存在的实例,或者你希望用户离开任务时清空所有的回退堆栈。这些都可以用<activity>元素的属性来实现。
一个activity可以启动另外一个程序的activity,让用户感觉就像是一个程序一样,Android让两个activity完成一个任务,让用户感觉到无缝的操作体验。
一个任务包含了一组activity,这些activity被分配在回退堆栈中,并且被按照顺序打开。
当用户点击程序图标时,程序的任务被展示到前台,如果当前的程序没有任务正在运行,那么一个新的任务会被创建,“main”activity会被运行,这个activity也被作为根activity放入到后退堆栈中。
当当前activity启动另外一个activity时,新的activity被放入堆栈的顶部,并取得焦点,先前的activity被保持在堆栈中,但是出于被停止状态,这个状态下,系统会保持用户的当前状态,当用户按back键返回时,当前状态会被恢复,前面启动的那个activity会被弹出堆栈并被销毁。activity在堆栈中是不会被重新排序的,他遵循“后进,先出”规则。下图展示了堆栈的每个时间段的状态:
![](https://img-my.csdn.net/uploads/201209/05/1346850703_2090.png)
如果用户一直按back键,直到回到主屏幕,那么所有的activity都会被移除堆栈,这个任务就不存在了。
一个任务是一个内聚的单元,当用户点击Home按钮或者开始一个新的任务时,先前的任务可以移动到“后台”。当这个任务处于后台时,任务中的activity都处于停止状态,但是后退堆栈仍然完整的保存着它们,只是失去了焦点而已。如下图所示:
![](https://img-my.csdn.net/uploads/201209/05/1346850596_6863.png)
一个任务可以从后台返回前台,以便用户可以从中断任务的地方重新开始。
提示:后台可以同时存在多个任务,但是系统在回收内存的时候可能会销毁一些后台的activity,所以一些后台的activity状态会丢失。
因为堆栈不会被重新排序,如果用户允许从多个地方启动一个特别的activity的话,堆栈中会出现同一个activity的不同实例,当用户点击back键的时候,这些实例都会被释放。不过你也可以避免这种情况,就是让activity同时只能被实例化一次,下面的章节会讨论到。
![](https://img-my.csdn.net/uploads/201209/05/1346850749_7220.png)
下面总结了activity和task的默认行为:
- 当activity A开启activity B时,A被停止,系统保持A的状态。如果用户按back键退出B,A恢复状态。
- 当用户按Home键离开一个任务,当前activity被停止,任务进入后台。系统保持任务中所有activity的状态。如果用户重新回到任务,那么任何回到前台,堆栈中顶部的activity恢复。
- 如果用户按back键,当前activity被推出堆栈,然后被销毁。先前的activity被恢复。当一个activity被销毁了,系统将不再保持它的状态。
- Activity可以在不同的任务中被多次实例化。
保存activity状态
当activity被停止时,系统可能会销毁它,当是系统仍然知道这个activity在回退堆栈中,这种情况下,系统就必须重新创建这个activity,为了避免用户状态丢失,我们应该实现OnSaveInstanceState()方法。详细用法看activity章节。
管理任务
你可能希望在每次启动一个程序中的activity时都开始一个新的任务,而不是继续当前任务,或者你想在启动一个activity时调用已经存在的实例,或者你希望用户离开任务时清空所有的回退堆栈。这些都可以用<activity>元素的属性来实现。
你可以使用下面这些主要的<activity>来实现:
你也可以使用下面这些主要的intent标志:
下面的内容会展示这些属性的用法。
警告:大多数情况不需要改变默认的activity和task行为。如果必须改变的话,你需要认真测试它的可用性。保证修改后的行为和用户期望的行为不冲突。
定义启动模式
启动模式允许你定义一个新的activity实例是怎么和当前任务关联的。你可以使用下面两中方法定义启动模式:
1. 使用清单文件。
2. 使用Intent标志。
例如,如果activity A启动了activity B,B在清单文件中定义了它和当前任务的关联方式,不过A也通过intent标志指定了B的关联方式,这种情况下,A的请求会被优先考虑。
使用清单文件
可以使用<activity>的<launchMode>属性来指定activity是怎么启动到一个task中的。下面是不同的启动模式:
"standard"(默认模式)
"singleTop"当程序启动时,或者使用intent启动activity时,系统创建一个新的activity实例到task中。这个activity可以被同时多次实例化,每个实例可以属于不同的task,一个task也可以包含它的多个实例。
"singleTask"如果activity的实例已经存在于当前堆栈的顶部,那么系统会通过onNewIntent()来实例化activity,而不是创建一个新的activity实例。这个activity可以被同时多次实例化,每个实例可以属于不同的task,一个task也可以包含它的多个实例。
例如,一个任务包含4个activity,顺序是ABCD,D处于堆栈顶部,如果D的启动方式是默认的standard的话,一个intent启动D时会创建一个新的D的实例,堆栈会是ABCDD,如果D的启动模式是singleTop,那么由于D已经存在于堆栈的顶部了,那么D的实例通过onNewIntent()接受intent,堆栈保持ABCD的队列。但是如果是B是singleTop,不过B不处于堆栈顶部,所以启动时为ABCDB。
提示:当一个新activity实例被创建,用户可以按back按钮返回先前的activity,当时当一个已经存在的activity实例被新的intent处理时,用户按back键时无法返回到新的intent被onNewIntent()接受前的状态。
"singleIntance"系统创建一个新的任务,实例化一个activity做为根activity。如果任务已经存在,并且intent对应的activity也已经实例化,那么会像上面一样调用onNewIntent()方法来处理新的intent。
提示:虽然activity开始在一个新的任务中,但是back按钮仍然返回先前的activity。
我们看另外一个实例,Android浏览器程序声明了浏览器activity必须在自己的任务中被打开 - 在<activity>元素中声明singleTask来实现。意思是,如果你的程序发送一个intent来打开浏览器,这个activity不会在你的程序任务中被打开,而是开启一个新的浏览器任务,如果浏览器在后台已经运行着了,那么会回到前台,并处理这个新的intent。任务中只包含一个activity的实例,任何activity启动这个activity就相当于打开一个任务。
不管一个activity是在新的任务中被开启,还是在当前任务被开启,Back按钮通常让用户回到先前的activity。不过,如果你开启的activity指定了singleTask启动模式,并且这个activity已经存在于后台堆栈中,那么整个后台堆栈的任务都会被放入前台,包括任务中包含的所有activity。下图展示了这个过程:
![](https://img-my.csdn.net/uploads/201209/05/1346850813_4587.png)
使用Intent标志
当启动activity时,你可以使用intent中的flag来修改默认的activity关联,然后传递到startActivity()中:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_SINGLE_TOP在新task中启动activity。如果一个task已经运行了这个activity,这个task会使用使用最后的状态恢复到前台,然后activity在onNewIntent()中接收新的intent。
类似前面说的singleTask启动模式。
FLAG_ACTIVITY_CLEAR_TOP和上面说的singleTo一样。
处理affinity如果一个activity已经存在于当前task,而现在需要启动它,那么不好创建一个新的activity实例,而是销毁它上面的所有activity,然后恢复它到堆栈顶部,然后调用onNewIntent()方法。
FLAG_ACTIVITY_CLEAR_TOP通常和FLAG_ACTIVITY_NEW_TASK一起使用。用来定位另外一个task中已经存在的activity,然后把它放到能够接收intent的位置。
affinity指定了activity更加优先属于那个任务。默认情况下,相同程序中所有的activity彼此间都有一个affinity,所以,所有的activity在相同的任务中有一样的优先级。不过,你可以修改activity默认的affinity。定义在不同程序中的activity可以分享一个affinity,或者定义在相同程序中的activity可以被分配不同的任务affinity。
你可以使用<activity>的taskAffinity属性来修改affinity。
taskAffinity属性值为一个字符串,必须是唯一的,而且和<manifest>元素的包名不同,因为系统会使用这个包名来指定程序的默认任务affinity。
affinity在下面两种情况下发挥作用:
- 当启动activity的intent包含FLAG_ACTIVITY_NEW_TASK标志时。
- 当activity的allowTaskReparenting属性为true时。
清除后退堆栈
在用户长时间离开一个任务时,系统会清理除了根activity外的所有activity。因为过了比较长时间后,用户一般都会忘了先前的任务进度,然后希望从新开始一个任务。
下面这些activity属性可以被用来修改这些行为:
alwaysRetainTaskState
clearTaskOnLaunch在根activity中为true的话,系统会一直保持后退堆栈中的所有activity。
finishOnTaskLaunch在根activity中为true的话,用户离开任务时,所有的activity都被清除,不管离开的时间的长短。
开始一个任务和clearTaskOnLaunch类似,不过是用来处理一个activity的,当设置为true时,这个activity只会在当前会话保持状态。用户离开task后再回来时,这个activity就不存在了。
你可以通过设置intent过滤器中的动作为"android.intent.action.MAIN",类别为"android.intent.category.LAUNCHER"来声明这个activity为任务的入口:
<activity ... > <intent-filter ... > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> ... </activity>
这个activity的icon和label属性会被展示在程序列表中。用户可以点击图标启动这个activity,或者回到已经创建的任务。
第二个重要的能力是:用户需要使用这个activity回到已经启动的任务。singleTask和singleInstance模式的activity通常用来初始化一个任务,所以应该用在设置了ACTION_MAIN和CATEGORY_LAUNCHER过滤器的activity中。想象一下,如果没有这个过滤器,那么程序清单中就不会展示这个程序,一个intent启动了一个singleTask的activity,开始了一个新的任务,当用户按home按钮时,这个任务被移到后台,并且不可见。现在用户就没有方法可以回到这个任务中了。