启动模式允许开发者定义一个activity的新实例如何与当前的Task关联。可以定义使用俩种方法来定义。
如果Activity A开启Activity B, Activity B就可以在它的manifest文件中定义它与当前的task如何关联,Activity A也可以要求activity B应该如何与当前的task关联。如果两个activity都定义了Activity B应该如何与一个task关联,Activity A的要求(在intent中定义的)将会覆盖Activity B中要求(在manifest文件中定义的)。
注意:一些在manifest中的登录模式在intent中不再可用,同样地,一些在intent中定义的标志也可能没有在manifest中未定义。
Using the manifest file
当在manifest文件中声明activity时,可以指定这个activity开启时如何与当前task关联。
<activity>标签的launchMode属性可以设置为四种不同的模式:
“standard”(默认模式)
“singleTop”
“singleTask”
“singleInstance”
这几种模式的区别体现以下四点上:
1)当这个activity被激活的时候,会放入哪个任务栈。
对于“standard”和“singleTop”模式,这个新被激活的activity会放入和之前的activity相同的任务栈中――除非Intent对象包含FLAG_ACTIVITY_NEW_TASK标志。
“singleTask”并不会每次都新启动一个task。如果已经存在一个task与新activity亲和度(taskAffinity)一样,则activity将启动到该task。如果不是,才启动一个新task。同一个application里面,每个activity的taskAffinity默认都是一样的。
“singleInstance”模式则表示这个新被激活的activity会重新开启一个任务栈,并作为这个新的任务栈的唯一的activity。
2)是否可以存在这个activity类型的多个实例。
对于“standard”和“singleTop”模式,可以有多个实例,并且这些实例可以属于不同的任务栈,每个任务栈也可以包含有这个activity类型的多个实例。
“singleTop"要求如果创建intent的时候栈顶已经有要创建 的Activity的实例,则将intent发送给该实例,而不发送给新的实例。
“singleTask”和”singleInstance”则限制只生成一个实例。
3)包含此activity的任务栈是否可以包含其它的activity。
“singleInstance”模式表示包含此activity的任务栈不可以包含其它的activity。若此activity启动了另一个activity组件,那么无论那个activity组件的启动模式是什么或是Intent对象中是否包含了FLAG_ACTIVITY_NEW_TASK标志,它都会被放入另外的任务栈。在其它方面“singleInstance”模式和“singleTask”模式是一样的。
其余三种启动模式则允许包含此activity的任务栈包含其它的activity。
4)是否每次都生成新实例
对于默认的“standard”模式,每当响应一个Intent对象,都会创建一个这种activity类型的新的实例。即每一个activity实例处理一个intent。
对于“singleTop”模式,只有当这个activity的实例当前处于任务栈的栈顶位置,则它会被重复利用来处理新到达的intent对象。否则就和“standard”模式的行为一样。
“singleInstance”是其所在栈的唯一activity,它会每次都被重用。
对于“singleTask”模式的acitvity,在其上面可能存在其它的activity组件,所以它的位置并不是栈顶,在这种情况下,会清理该activity上面的activity,把该activity放在栈顶,并把task切换到前台。
(一种特殊情况)当一个activity-A启动时,如果需要为它创建一个task(如果一个activity-A是singleTask或者是singleInstance,或者intent指定FLAG_ACTIVITY_NEW_TASK,并且和activity-A的affinity对应的task不存在),那么这个新的task就是和这个activity-A的启动参数(userId, uid, callingPackage, affinity, intent, realActivity)相对应的。如果这个Activity-A之后又启动了其他的Activity-B,并且在启动了Activity-B之后,Activity-A结束了,那么当前的task中就只剩下Activity-B。这时如果以同样的方式再启动Activity-A,那么intent也是会被丢弃,直接把已经存在的task切换到前台,而不去启动Activity-A。
注意:
当已经存在的activity实例处理新的intent时候,会调用onNewIntent()方法
若为了处理一个新到达的intent对象而创建了一个activity实例,则用户按下“BACK”键就会退到之前的那个activity。但若这个新到达的intent对象是由一个已经存在的activity组件来处理的,那么用户按下“BACK” 键就不会回退到处理这个新intent对象之前的状态了。
Using Intent flags
当开启一个activity时,可以通过在intent中包含标志来修改activity的默认的与当前task的关联,然后将该intent传递给startActivity().可以修改的默认的标志为:
- FLAG_ACTIVITY_NEW_TASK
在一个新的task中开启一个activity。如果包含该activity的task已经运行,该task就回到前台,activity通过onNewIntent()接受处理该intent,如果要启动的activity不是栈顶元素,只会resume栈顶的元素。设置此状态,记住以下原则,首先会查找是否存在和被启动的Activity具有相同的亲和性的任务栈(即taskAffinity,注意同一个应用程序中的activity的亲和性一样,所以下面的a情况会在同一个栈中,前面这句话有点拗口,请多读几遍),如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的activity顺序不变,如果没有,则新建一个栈来存放被启动的activity
这是与"singleTask"登模式相同的行为,区别是启动"singleTask"模式的activity会清空它之上的activity(从home启动时会清空,从recents启动时不会清空)。 - FLAG_ACTIVITY_SINGLE_TOP
如果要被开启的activity是当前的activity(在返回栈的顶部),已经存在的实例通过onNewIntent()接收一个调用,然后处理该intent,而非重新创建一个新的实例。
这与"singleTop"登录模式有相同的行为。 - FLAG_ACTIVITY_CLEAR_TOP
如果要被开启的activity已经在当前的task中运行,系统不会生成该activity的一个新的实例,在该栈顶部的所有其他的activity会被销毁,这个intent通过onNewIntent()被传递给该重新运行的activity的实例(现在在栈顶部)。
manifest中没有相对应的属性。
FLAG_ACTIVITY_CLEAR_TOP经常和FLAG_ACTIVITY_NEW_TASK一起使用.当一起使用时,这些标志可以确定一个存在的activity在另一个task中的位置,并且将其放置于可以响应intent的位置(FLAG_ACTIVITY_NEW_TASK确定该activity,然后FLAG_ACTIVITY_CLEAR_TOP销毁顶部其他的activity)。如果指定的activity的登录模式是"standard",也会被从栈中移除,一个新的实例也会被登录到它的位置来处理到来的intent。那是因为当登录模式为 "standard"时,一个新的实例总是被创建 - FLAG_ACTIVITY_NEW_DOCUMENT
- 一个activity在mainfest文件中添加 属性:android:documentLaunchMode 时,该activity被启动时永远会创建一个新的task。该属性有4个值,用户在应用中打开一个document时会有不同的效果:
- intoExisting: activity 会为该document请求一个已经存在的task,这与 设置FLAG_ACTIVITY_NEW_DOCUMENT 且不设置 FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果
- always: activity 会为该document创建一个新的task,即使该document已经被打开了,这与设置 FLAG_ACTIVITY_NEW_DOCUMENT 且设置 FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果
- none:activity 不会为 document 创建新的task,该app被设置为 single task 的模式,它会重新调用用户唤醒的所有activity中的最近的一个
- never:activity 不会为document创建一个新的task,设置这个值复写了 FLAG_ACTIVITY_NEW_DOCUMENT 和 FLAG_ACTIVITY_MULTIPLE_TASK 标签。如果其中一个标签被设置,并且overview screen 显示该app为 single task 模式。 则该activity 会重新调用用户最近唤醒的activity
- 注意: none 或 nerver 使用时,activity必须设置为 launchMode=”standard” ,如果该属性没有设置,documentLaunchMode=”none” 属性就会被使用。
注意: 其实以上的解释一起用非常复杂,比如一般系统默认activity是 standard,但当我activity代码设置为FLAG_ACTIVITY_NEW_TASK时仍然还是生成新的activity,当设置FLAG_ACTIVITY_CLEAR_TOP 时也是生成新的activity,当FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK时也是生成新的activity,或许这句好是经典“登录模式为 "standard"时,一个新的实例总是被创建”,以下的两种方式是我经常用的。
Activity的两种启动模式:FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT
1. 如果已经启动了四个Activity:A,B,C和D。在D Activity里,我们要跳到B Activity,同时希望C finish掉,可以在startActivity(intent)里的intent里添加flags标记,如下所示:
- Intent intent = new Intent(this, B.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
这样启动B Activity,就会把D,C都finished掉,如果你的B Activity的启动模式是默认的(multiple) ,则B Activity会finished掉,再启动一个新的Activity B。
如果不想重新再创建一个新的B Activity,则在上面的代码里再加上:
- intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
这样B Activity就会再创建一个新的了,而是会重用之前的B Activity,同时调用B Activity的onNewIntent()方法。
2. 如果已经启动了四个Activity:A,B,C和D,在D Activity里,想再启动一个Actvity B,但不变成A,B,C,D,B,而是希望是A,C,D,B,则可以像下面写代码:
- Intent intent = new Intent(this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- startActivity(intent);
在Android应用程序开发的时候,从一个Activity启动另一个Activity并传递一些数据到新的Activity上非常简单,但是当您需要让后台运行的Activity回到前台并传递一些数据可能就会存在一点点小问题。
首先,在默认情况下,当您通过Intent启到一个Activity的时候,就算已经存在一个相同的正在运行的Activity,系统都会创建一个新的Activity实例并显示出来。为了不让Activity实例化多次,我们需要通过在AndroidManifest.xml配置activity的加载方式(launchMode)以实现单任务模式,如下所示:
1 <activity android:label="@string/app_name" android:launchmode="singleTask"android:name="Activity1">
2 </activity>
launchMode为singleTask的时候,通过Intent启到一个Activity,如果系统已经存在一个实例,系统就会将请求发送到这个实例上,但这个时候,系统就不会再调用通常情况下我们处理请求数据的onCreate方法,而是调用onNewIntent方法,如下所示:
1 protected void onNewIntent(Intent intent) {
2 super.onNewIntent(intent);
3 setIntent(intent);//must store the new intent unless getIntent() will return the old one
4 processExtraData();
5 }
不要忘记,系统可能会随时杀掉后台运行的Activity,如果这一切发生,那么系统就会调用onCreate方法,而不调用onNewIntent方法,一个好的解决方法就是在onCreate和onNewIntent方法中调用同一个处理数据的方法,如下所示:
01 public void onCreate(Bundle savedInstanceState) {
02 super.onCreate(savedInstanceState);
03 setContentView(R.layout.main);
04 processExtraData();
05 }
06
07 protected void onNewIntent(Intent intent) {
08 super.onNewIntent(intent);
09 setIntent(intent);//must store the new intent unless getIntent() will return the old one
10 processExtraData()
11 }
12
13 private void processExtraData(){
14 Intent intent = getIntent();
15 //use the data received here
16 }
Activity的各种特性对应task的各种表现举例
一般情况:
(1) 前提: Activity A和Activity B在同一个应用中.
操作: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B(堆栈状态: AB), 按下BACK返回键(堆栈状态: A).
(2) 前提: Activity A和Activity B在同一个应用中, 应用名称为"TaskOne应用".
操作: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity B(TaskA堆栈状态: AB), 长按Home键, 返回Launcher, 启动其它应用(如:电子书),
开僻一个新Task堆栈, 命名: TaskB, 长按Home健, 返回Launcher, 单击"TaskOne应用"图标, 此时TaskA堆栈返回前台,
Activity B为栈顶应用, 供用户使用.
(3) 前提: Activity A在名称为"TaskOne应用"的应用中, Activity C在名称为"TaskTwo应用"的应用中.
操作: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity C(TaskA堆栈状态: AC),长按Home键, 返回Launcher, 启动"TaskTwo应用"即Activity C,
开僻新的Task堆栈, 命名为TaskB, 按BACK键返回Launcher, 单击"TaskOne应用"图标, 此时TaskA堆栈返回前台,
Activity C为栈顶应用, 供用户使用.
Intent中的flag配置举例:
(1) FLAG_ACTIVITY_NEW_TASK: 设置此状态,记住以下原则,首先会查找是否存在和被启动的Activity具有相同的亲和性的任务栈(即taskAffinity,注意同一个应用程序中的activity的亲和性一样,所以下面的a情况会在同一个栈中,前面这句话有点拗口,请多读几遍),如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的activity顺序不变,如果没有,则新建一个栈来存放被启动的activity
a. 前提: Activity A和Activity B在同一个应用中.
操作: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B, 启动Activity B的Intent的Flag设为
FLAG_ACTIVITY_NEW_TASK, Activity B被压入Activity A所在堆栈(堆栈状态: AB).
原因: 默认情况下同一个应用中的所有Activity拥有相同的关系(taskAffinity).
b. 前提: Activity A在名称为"TaskOne应用"的应用中, Activity C和Activity D在名称为"TaskTwo应用"的应用中.
操作1: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity C, 启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,
Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 长按Home键, 选择TaskA,
Activity A回到前台, 再次启动Activity C(两种情况1.从桌面启动;2.从Activity A启动,两种情况一样), 这时TaskB回到前台, Activity C显示, 供用户使用, 即:
包含FLAG_ACTIVITY_NEW_TASK的Intent启动Activity的Task正在运行, 则不会为该Activity创建新的Task,
而是将原有的Task返回到前台显示.
操作2: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity C,启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,
Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 在Activity C中启动
Activity D(TaskB的状态: CD) 长按Home键, 选择TaskA, Activity A回到前台, 再次启动Activity C(从桌面或者ActivityA启动,也是一样的),
这时TaskB回到前台, Activity D显示,供用户使用.说明了在此种情况下设置FLAG_ACTIVITY_NEW_TASK后,会先查找是不是有Activity C存在的栈,根据亲和性(taskAffinity),如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的顺序不变。
(2) FLAG_ACTIVITY_CLEAR_TOP:
前提: Activity A, Activity B, Activity C和Activity D在同一个应用中.
操作: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B(堆栈状态: AB), 在Activity B中启动
Activity C(堆栈状态: ABC), 在Activity C中启动Activity D(堆栈状态: ABCD), 在Activity D中启动Activity B,
启动Activity B的Intent的Flag设置为FLAG_ACTIVITY_CLEAR_TOP, (堆栈状态: AB).虽然现在堆栈中为AB,但是这时的B是否和之前是同一个对象,取决于B自身的launchMode,如果是standard,则现在的B为一个新建的B,否则为原来的对象。
(3) FLAG_ACTIVITY_BROUGHT_TO_FRONT: (把activity的task带入前台,并清除activity之上的activity,类似于singleTask)
前提: Activity A在名称为"TaskOne应用"的应用中, Activity C和Activity D在名称为"TaskTwo应用"的应用中.
操作: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity C,启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,
Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 在Activity C中启动
Activity D(TaskB的堆栈状态: CD), 长按Home键, 选择TaskA, Activity A回到前台, 在Activity A中再次启动Activity C,
在启动Activity C的Intent中设置Flag为FLAG_ACTIVITY_BROUGHT_TO_FRONT, TaskB回到前台,
Activity C显示, (TaskB的堆栈状态: C).
(4) FLAG_ACTIVITY_MULTIPLE_TASK:
与FLAG_ACTIVITY_NEW_TASK结合使用, 首先在Intent中设置FLAG_ACTIVITY_NEW_TASK, 打开Activity,
则启动一个新Task, 接着在Intent中设置FLAG_ACTIVITY_MULTIPLE_TASK, 再次打开同一个Activity,则还会新启动一个Task.
(5) FLAG_ACTIVITY_SINGLE_TOP:
当前Task堆栈中存在ABCD四个Activity, A是栈顶Activity, D为栈底Activity, 存在打开A的Intent中设置了
FLAG_ACTIVITY_SINGLE_TOP标志, 则会使用栈顶A, 而不会从新New A.
(6) FLAG_ACTIVITY_RESET_TASK_IF_NEEDED:
一般与FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET结合使用,如果设置该属性,这个activity将在一个新的task中启动或者或者被带到一个已经存在的task的顶部,这时这个activity将会作为这个task的首个页面加载。将会导致与这个应用具有相同亲和力的task处于一个合适的状态(移动activity到这个task或者从中移出),或者简单的重置这个task到它的初始状态
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET:在当前的Task堆栈中设置一个还原点,当带有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent请求启动这个堆栈时(典型的例子是用户从桌面再次启动这个应用),还原点之上包括这个应用将会被清除。应用场景:在email程序中预览图片时,会启动图片观览的activity,当用户离开email处理其他事情,然后下次再次从home进入email时,我们呈现给用户的应该是上次email的会话,而不是图片观览,这样才不会给用户造成困惑。
例: 存在Activity A, Activity B, Activity C, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity B(TaskA堆栈状态: AB), 接着Activity B启动Activity C(TaskA堆栈状态: ABC),
启动Activity C的Intent中设置FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET标志, 这样TaskA中有一个还原点,
当有包含FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent请求TaskA堆栈时(比如请求Activity A)
系统就会将还原点以上的Activity清除, TaskA堆栈中只剩下了AB.
taskAffinity属性:
(1) taskAffinity属性应和FLAG_ACTIVITY_NEW_TASK标志及allowTaskReparenting属性结合使用, 如果只使用taskAffinity属性,
请参考上面Task默认的行为.
(2) 与FLAG_ACTIVITY_NEW_TASK标志结合:
a. 前题: Activity A和Activity B在同一个应用中, Activity A与Activity B设置不同的taskAffinity属性.
操作: Activity A启动开僻Task堆栈,命名为TaskA(TaskA堆栈状态: A), 在Activity A中启动Activity B, 启动Activity B
的Intent中设置FLAG_ACTIVITY_NEW_TASK标志,这时系统会新开僻一个Task堆栈,TaskB(TaskB堆栈状态: B).
b. 前题: Activity A在"TaskOne应用"中, Activity C在"TaskTwo应用"中, Activity A和ActivityC设置了相同的taskAffinity属性.
操作: Activity A启动开僻Task堆栈,命名为TaskA(TaskA堆栈状态: A), 在Activity A中启动Activity C, 启动Activity C的
Intent中设置FLAG_ACTIVITY_NEW_TASK标志,这时Activity C会压入与Activity A堆栈相同的TaskA堆栈(TaskA堆栈状态: AC).
(3) 与allowTaskReparenting属性:
例: 在"TaskOne应用"中有一个天气预报Activity A, Activity A与"TaskOne应用"中的其它Activity有默认的关系
(taskAffinity属性都没有设置), 并且allowTaskReparenting属性设置为true, 现在存在一个"TaskTwo应用
"启动了"TaskOne应用"中的天气预报Activity A, 这时Activity A与"TaskTwo应用"中的Activity在同一个Task,
命名这个Task堆栈为TaskA, 这时"TaskOne应用"启动, 并且又打开发天气预报Activity A, 这时Activity A会从TaskA堆栈中转移到
"TaskOne应用"所在的堆栈, 即Activity A可以在多个堆栈中来回转移.
下面三个属性都是在启动该activity的intent中带有“Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED”标志时才有作用。一般来讲,从launcher启动app时,intent都带有这个标志。
alwaysRetainTaskState属性:
如果Task堆栈中的Root Activity设置了此属性值为true, 不管出现任何情况, 一直会保留Task栈中Activity的状态.
clearTaskOnLaunch属性:
这个标志只对task中的Root Activity起作用。如果Task堆栈中的Root Activity设置了此属性值为true, 当task被切换到后台,然后又被切换回来时,AMS会清除这个Root Activity上面的所有activity。
具体的实现在resetTaskIfNeededLocked函数中(ActivityStack.java 文件中)。
final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop, ActivityRecord newActivity)
和这个属性相似的属性:
launchMode="singleTask" ------ 如果一个task包含singleTask的activity,当这个task被切换到后台,之后系统又要resume 该activity时,系统会把包含这个activity的task切换到前台,并清除该activity上面的所有activity。
Intent中的FLAG_ACTIVITY_CLEAR_TOP ------ 如果当前task中存在要启动的activity,就把该activity上面的activity都清除。至于已经存在的目标activity,是resume,还是销毁了重新创建,取决于这个activity的launchMode。非standard模式的就resume,如果是standard模式,并且intent中没有FLAG_ACTIVITY_SINGLE_TOP标志的,就销毁重建。
finishOnTaskLaunch属性:
如果某Activity设置了finishOnTaskLaunch属性, 当task被切换到后台,然后又被切换回来时,AMS会清除这个有finishOnTaskLaunch属性的activity。和finishOnTaskLaunch属性相比,这个属性是针对单个activity起作用的。