Activity的启动模式 和 Intent启动选项

释放双眼,带上耳机,听听看~!

启动模式允许开发者定义一个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
       如果指定了这个flag,则在一个新的task中启动activity。如果想更精确的控制启动的task,可以使用documentlaunchmode属性。
  • 一个activity在mainfest文件中添加 属性:android:documentLaunchMode 时,该activity被启动时永远会创建一个新的task。该属性有4个值,用户在应用中打开一个document时会有不同的效果:
  1. intoExisting: activity 会为该document请求一个已经存在的task,这与 设置FLAG_ACTIVITY_NEW_DOCUMENT 且不设置 FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果
  2. always: activity 会为该document创建一个新的task,即使该document已经被打开了,这与设置 FLAG_ACTIVITY_NEW_DOCUMENT 且设置 FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果
  3. none:activity 不会为 document 创建新的task,该app被设置为 single task 的模式,它会重新调用用户唤醒的所有activity中的最近的一个
  4. never:activity 不会为document创建一个新的task,设置这个值复写了 FLAG_ACTIVITY_NEW_DOCUMENT 和 FLAG_ACTIVITY_MULTIPLE_TASK 标签。如果其中一个标签被设置,并且overview screen 显示该app为 single task 模式。 则该activity 会重新调用用户最近唤醒的activity
  5. 注意: none 或 nerver 使用时,activity必须设置为 launchMode=”standard” ,如果该属性没有设置,documentLaunchMode=”none” 属性就会被使用。

如果想要在overview screen 中保留一个task,即使它所属的activity 已经 finish 了,可以在要启动该activity的Intent中添加flag。你也可以通过调用finishAndRemoveTask()
函数描述何时移除task 并结束所有的activitys 之间的关联。使用finishAndRemoveTask() 函数的方法复写了 FLAG_ACTIVITY_RETAIN_IN_RECENTS flag。

 

注意: 其实以上的解释一起用非常复杂,比如一般系统默认activity是 standard,但当我activity代码设置为FLAG_ACTIVITY_NEW_TASK时仍然还是生成新的activity,当设置FLAG_ACTIVITY_CLEAR_TOP
时也是生成新的activity,当FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_NEW_TASK时也是生成新的activity,或许这句好是经典“登录模式为
“standard”时,一个新的实例总是被创建”,以下的两种方式是我经常用的。

 

Activity的两种启动模式:FLAG_ACTIVITY_CLEAR_TOPFLAG_ACTIVITY_REORDER_TO_FRONT

1. 如果已经启动了四个Activity:A,B,C和D。在D Activity里,我们要跳到B Activity,同时希望C finish掉,可以在startActivity(intent)里的intent里添加flags标记,如下所示:

  1. Intent intent = new Intent(this, B.class);
  2. intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  3. startActivity(intent);

这样启动B Activity,就会把D,C都finished掉,如果你的B Activity的启动模式是默认的(multiple) ,则B
Activity会finished掉,再启动一个新的Activity B。

如果不想重新再创建一个新的B Activity,则在上面的代码里再加上:

  1. 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)以实现单任务模式,如下所示:

<activity android:label="@string/app_name" android:launchmode="singleTask"android:name="Activity1">

 </activity>

 

launchMode为singleTask的时候,通过Intent启到一个Activity,如果系统已经存在一个实例,系统就会将请求发送到这个实例上,但这个时候,系统就不会再调用通常情况下我们处理请求数据的onCreate方法,而是调用onNewIntent方法,如下所示:

protected void onNewIntent(Intent intent) {

 super.onNewIntent(intent);

 setIntent(intent);//must store the new intent unless getIntent() will
return the old one

  processExtraData();

}

 

不要忘记,系统可能会随时杀掉后台运行的Activity,如果这一切发生,那么系统就会调用onCreate方法,而不调用onNewIntent方法,一个好的解决方法就是在onCreate和onNewIntent方法中调用同一个处理数据的方法,如下所示:

 

public void onCreate(Bundle savedInstanceState) {

   super.onCreate(savedInstanceState);

  setContentView(R.layout.main);

   processExtraData();

 }



 protected void onNewIntent(Intent intent) {

   super.onNewIntent(intent);

   setIntent(intent);//must store the new intent unless getIntent() will
return the old one

   processExtraData()

}



 private void processExtraData(){

   Intent intent = getIntent();

   //use the data received here

 }

 

 

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起作用的。

 

人已赞赏
Android文章

Android View 事件分发机制源码详解(ViewGroup篇)

2020-4-26 20:48:12

Android文章

Android应用程序资源的查找过程分析

2020-4-26 23:18:02

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索