Activity启动模式

来源:互联网 发布:淘宝网松木床架.. 编辑:程序博客网 时间:2024/06/05 21:09

本文是主要借鉴了一下几篇文章:
官方文档
http://blog.csdn.net/guolin_blog/article/details/41087993 郭霖
http://blog.csdn.net/singwhatiwanna/article/details/9294285 任玉刚
http://www.cnblogs.com/lwbqqyumidi/p/3775479.html
一个应用程序中如果有多个入口都可以启动同一个Activity,那么每次启动的时候就都会创建该Activity的一个新的实例,而不是将返回栈中的Activity移到栈顶。那么就导致同一个Activity被多次实例。如果不希望一个Activity被多次实例化,这就涉及到Activity的启动模式了。
可以在AndroidManifest.xml配置的android:launchMode属性或通过Intent flags设置。
在元素中,有以下几个属性是可以使用的:

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

而在Intent当中,有以下几个flag是比较常用的:

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

下面我们就将开始讨论,如何通过AndroidManifest.xml配置以及Intent的flags来改变Activity在任务中的默认行为。

定义启动模式

启动模式允许你去定义如何将一个Activity的实例和当前的任务进行关联,你可以通过以下两种不同的方式来定义启动模式:

  1. 使用manifest文件

    当你在manifest文件中声明一个Activity的时候,你可以指定这个Activity在启动的时候该如何与任务进行关联。

  2. 使用Intent flag

    当你调用startActivity()方法时,你可以在Intent中加入一个flag,从而指定新启动的Activity该如何与当前任务进行关联。

也就是说,如果Activity A启动了Activity B,Activity B可以定义自己该如何与当前任务进行关联,而Activity A也可以要求Activity B该如何与当前任务进行关联。如果Activity B在manifest中已经定义了该如何与任务进行关联,而Activity A同时也在Intent中要求了Activity B该怎么样与当前任务进行关联,那么此时Intent中的定义将覆盖manifest中的定义。

需要注意的是,有些启动模式在manifest中可以指定,但在Intent中是指定不了的。同样,也有些启动模式在Intent中可以指定,但在manifest中是指定不了的,下面我们就来具体讨论一下。

使用manifest文件

当在manifest文件中定义Activity的时候,你可以通过元素的launchMode属性来指定这个Activity应该如何与任务进行关联。launchMode属性一共有以下四种可选参数:

standard

standard是默认的启动模式,即如果不指定launchMode属性,则自动就会使用这种启动模式。这种启动模式表示每次启动该Activity时系统都会创建一个新的实例,并且总会把它放入到当前的任务当中。声明成这种启动模式的Activity可以被实例化多次,一个任务当中也可以包含多个这种Activity的实例。

singleTop

该模式下,如果要启动的这个Activity实例处于当前任务栈顶的位置,那么系统就不会再去创建一个该Activity的实例,而是调用栈顶Activity实例的onNewIntent(Intent)方法(先是调用onPause()方法之后再调用该方法,然后onResume()方法),其中参数Intent为启动时传递的Intent对象。若不在栈顶,则创建一个新的Activity实例。声明成这种启动模式的Activity也可以被实例化多次若不在栈顶,则创建一个新的Activity实例。一个任务当中也可以包含多个这种Activity的实例。

singleTask

该模式下,分为两种情况:

  1. 该Activity和启动它的Activity属于同一个应用程序
    每次启动Activity时,系统首先检查Activity栈中是否存在该Activity实例,若存在,先把在该实例上面的所有Activity实例出栈,然后调用它的onNewIntent(Intent)方法(调用该方法的过程和singleTop一样),让该实例处于栈顶位置并显示;若不存在,则直接在相同的任务中创建一个Activity实例。
  2. 该Activity和启动它的Activity不属于同一个应用程序,或设置了该Activity的taskAffinity属性值
    系统会创建一个新的任务(若之前不存在),并将启动的Activity放入这个新任务的栈底位置,通过该Activity打开的其它Activity也会放入到该任务中(即使singleTop模式)。但是,如果现有任务当中已经存在一个该Activity的实例了,那么系统就不会再创建一次它的实例,而是会直接调用它的onNewIntent(Intent)方法(此时和第一种情况一样)。

注意:声明成这种启动模式的Activity,在同一个任务中只会存在一个实例。

singleInstance

这种启动模式无论是否设置taskAffinity属性值,都会在一个新的任务中创建,除非之前已经启动过该Activity实例(依然是通过onNewIntent()调用)。该Activity所在的任务中只有该Activity自己,通过该Activity打开的其它Activity会被放到别的任务栈(之前的任务中)中,即使其他Activity通过设置taskAffinity属性值为该任务也不会起作用。这样通过单独的Activity栈能够实现不同程序之间的数据共享问题。通过这种能够实现单例模式。

通过BACK键回退的话,只能回退到当前任务中的下一层Activity实例。即同一个任务中回退到下一层的Activity中,在任务A中启动一个任务B中的Activity实例,则按BACK键的话会在任务B中回退,而不是回退到任务A例。例如官方文档提供的例子:

launchMode

当然这里的BACK键是系统默认情况,你没有自行修改。

注意:

  • standard模式适合于绝大部分类型的Activity,singleTop也是普通的并很有用的启动模式。singleTask和singleInstance模式并不适用绝大多数的应用。
  • launchMode设置的启动模式会被Intent的flags覆盖。

使用Intent flags

除了使用manifest.xml配置文件之外,你也可以在调用startActivity(Intent)方法的时候,为Intent加入一个flag来改变Activity与任务的关联方式,可以通过Intent对象的setFlags(int)或addFlags(int)添加,前者设置一个,后者设置多个。若设置了flag则优先按照flag,其次launchMode。下面讲解一下几种重要的flag:

FLAG_ACTIVITY_NEW_TASK

官方文档说的这个标记和singleTask作用一样,那我也就暂时不管别人怎么说了。

FLAG_ACTIVITY_SINGLE_TOP

如果设置,当这个Activity位于当前任务栈(而并非任意任务栈)的栈顶时,不再启动一个新的,直接调用onNewIntent(),这个标记和singleTop作用一样。

注意:如果是从BroadcastReceiver启动一个新的Activity,或者是从Service往一个Activity跳转时,不要忘记添加Intent的Flag为FLAG_ACTIVITY_NEW_TASK??为什么

FLAG_ACTIVITY_CLEAR_TOP

设置了这个,如果要启动的Activity在当前任务中已经存在了,就不再次创建,而是会把这个Activity之上的所有Activity移除任务栈,使该实例显示在栈顶位置。该Activity可以通过调用onNewIntent()方法不销毁或者销毁重新创建。如果该Activity是”standard”模式,并且Intent中也没有加入一个FLAG_ACTIVITY_SINGLE_TOP标记,那么此时Activity B就会销毁掉,然后重新创建实例。而如果Activity B在manifest中指定了任何一种非默认模式,或者是在Intent中加入了一个FLAG_ACTIVITY_SINGLE_TOP 标记,那么就会调用onNewIntent()方法。

FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK结合在一起使用也会有比较好的效果,比如可以将一个后台运行的任务切换到前台,并把目标Activity之上的其它Activity全部关闭掉。这个功能在某些情况下非常有用,比如说从通知栏启动Activity的时候。

处理taskAffinity

taskAffinity可以用于指定一个Activity更加愿意依附于哪一个任务,在默认情况下,同一个应用程序中的所有Activity都具有相同的taskAffinity,所以,这些Activity都更加倾向于运行在相同的任务当中。当然了,你也可以去改变每个Activity的taskAffinity值,通过<activity>元素的taskAffinity属性就可以实现了。
taskAffinity属性接收一个字符串参数,你可以指定成任意的值(字符串中至少要包含一个‘.’),但必须不能和应用程序的包名相同,因为系统会使用包名来作为默认的taskAffinity值。

taskAffinity主要有以下两种应用场景:

  1. 当调用startActivity()方法来启动一个Activity时,默认是将它放入到当前的任务当中。但是,如果在Intent中加入了一个FLAG_ACTIVITY_NEW_TASK flag的话(或者该Activity在manifest文件中声明的启动模式是”singleTask”),系统就会尝试为这个Activity单独创建一个任务。但是规则并不是只有这么简单,系统会去检测要启动的这个Activity的taskAffinity和当前任务的affinity是否相同,如果相同的话就会把它放入到现有任务当中,如果不同则会去创建一个新的任务。而同一个程序中所有Activity的taskAffinity默认都是相同的,同一个应用程序中即使声明成”singleTask”,也不会为这个Activity再去创建一个新的任务了。

  2. 当把Activity的allowTaskReparenting属性设置成true时,Activity就拥有了一个转移所在任务的能力。具体点来说,就是一个Activity现在是处于某个任务当中的,但是它与另外一个任务具有相同的taskAffinity值,那么当另外这个任务切换到前台的时候,该Activity就可以转移到现在的这个任务当中。
    那还是举一个形象点的例子吧,比如有一个天气预报程序,它有一个Activity是专门用于显示天气信息的,这个Activity和该天气预报程序的所有其它Activity具体相同的taskAffinity值,并且还将allowTaskReparenting属性设置成true了。这个时候,你自己的应用程序通过Intent去启动了这个用于显示天气信息的Activity,那么此时这个Activity应该是和你的应用程序是在同一个任务当中的。但是当把天气预报程序切换到前台的时候,这个Activity又会被转移到天气预报程序的任务当中,并显示出来,因为它们拥有相同的taskAffinity值,并且将allowTaskReparenting属性设置成了true。

清空返回栈(或者称为管理后台Activity)

如果用户将任务切换到后台之后过了很长一段时间,系统会将这个任务中除了最底层的那个Activity之外的其它所有Activity全部清除掉。当用户重新回到这个任务的时候,最底层的那个Activity将得到恢复。这个是系统默认的行为,因为既然过了这么长的一段时间,用户很有可能早就忘记了当时正在做什么,那么重新回到这个任务的时候,基本上应该是要去做点新的事情了。

当然,既然说是默认的行为,那就说明我们肯定是有办法来改变的,在<activity>元素中设置以下几种属性就可以改变系统这一默认行为:

alwaysRetainTaskState

如果将最底层的那个Activity的这个属性设置为true,那么上面所描述的默认行为就将不会发生,任务中所有的Activity即使过了很长一段时间之后仍然会被继续保留。

clearTaskOnLaunch

如果将最底层的那个Activity的这个属性设置为true,那么只要用户离开了当前任务,再次返回的时候就会将最底层Activity之上的所有其它Activity全部清除掉。简单来讲,就是一种和alwaysRetainTaskState完全相反的工作模式,它保证每次返回任务的时候都会是一种初始化状态,即使用户仅仅离开了很短的一段时间。

finishOnTaskLaunch

这个属性和clearTaskOnLaunch是比较类似的,不过它不是作用于整个任务上的,而是作用于单个Activity上。如果某个Activity将这个属性设置成true,那么用户一旦离开了当前任务,再次返回时这个Activity就会被清除掉。

其它:

  1. taskAffinity字符串,必须有个”.”(不管在哪加,否则出错)若在同一个应用程序中一个singleTask的Activity设置了不同的taskAffinity属性,则该Activity实例就会在一个新的任务中,且在这之后的Activity实例都是在该任务栈中保存(如果其他Activity中的taskAffinity没有修改的话)

  2. onNewIntent调用时机
    onNewIntent只有在以下场景才会回调:当前通过Intent方式启动的Activity不是重新完整新建实例,而是复用之前已经存在的实例(FLAG_ACTIVITY_NEW_TASK,FLAG_ACTIVITY_CLEAR_TOP(这个如上,可以改变),FLAG_ACTIVITY_SINGLE_TOP,singleTask,singleTop,singleInstance中的一种。此时都会执行到onNewIntent())。onNewIntent()调用后,将继续回调onResume().

0 0
原创粉丝点击