Activity LaunchMode解析

Activity LaunchMode

LaunchMode

Android中Activity的启动模式有四种:

  • standard(默认选项)
  • singleTop
  • singleTask
  • singleInstance

可以在AndroidManifest.xml中通过指定activity标签的android:launchMode属性来改变:

<activity android:name=".SecondActivity"
    android:launchMode="singleTop"/>

如果不指定launchMode属性,那么activity会使用默认(standard)模式来运行

standard

standard是Activity的默认属性,Android在启动standard的Activity时遵循以下规则:

  1. 如果是点击图标进行启动,则会新创建一个task,然后将这个Activity压入这个task
  2. 如果是使用startActivty进行启动,则会将这个Activity压入启动它的那个Task中
  3. 默认情况下,Activity是多例模式,即一个Activity可以同时存在多个实例,也可以同时存在于多个task

详细说明

Activity的standard模式在自己App内部通常表示没有特殊的式样要求,遵循系统默认情况即可, 但是在多个App之间使用的时候需要注意, 它会被压入启动他的那个App的task中, 例如: 在电话App中选择添加联系人操作, 此时启动的是联系人App的添加联系人Activity,此时添加联系人Activity会被压入到电话App中,在用户看来,就好像是这个页面是属于电话App的.
因为从整个业务流程来看, 用户此时的心理预期是:

整个操作都属于电话App的流程, 而与联系人app无关

此时Android系统的表现为:

  1. 添加联系人页面的入场动画为Activity进入的动画,而不是task切换的动画(未指定动画的情况下)
  2. 当用户点击多任务键时, 在后台也看不到联系人App, 只能看到电话app
  3. 添加联系人页面中点击返回键,页面会回退到电话App的页面
  4. 如果此时用户手动打开了联系人App, 并再次添加联系人, 会得到一个全新的添加联系人页面, 并且与电话App中的`添加联系人页面没有冲突

singleTop

singleTop整体的行为与standard一致,区别如下:

  • 当Activity在入栈时,singleTop会检测当前task的栈顶Activity,如果当前的栈顶Activity与要压入的Activity是同一个Activity时,那么就不会创建Activity的实例,而是会直接使用当前的栈顶Activity,并且会调用Activity的onNewIntent方法,将新的Intent传入

singleTop只会检测栈顶的Activity,保证Activity在栈顶唯一,但是Activity还是可以以多例存在的

例如:
AActivity被设置为singleTop,AActivity启动自己,或其它应用继续启动AActivity,此时:

  1. AActivity在栈顶, 则不会创建AActivity,而是会调用AActivity的onNewIntent方法
    在栈顶

  2. AActivity不在栈顶, 则会重新创建一个AActivity的实例, 并执行其onCreate方法
    不在栈顶

singleTask

被标记为singleTask的Activity可以让该Activity在被别的Activity启动时,不会进入启动他的Task中,而是会在属于它自己的Task里创建,并放在栈顶, 然后把整个Task一起拿过来,压在启动Task上.

是否在当前Task中启动Activity,主要看taskAffinity:

  • 如果新的Activity的taskAffinity和当前Task相同,就继续在当前Task启动
  • 如果不同,就换到别的Task,如果有则利用那个task,如果没有就创建一个,找到对应task后:
    • 如果目标Task里没有目标Activity,创建Activity,入栈
    • 如果目标Task中已有目标Activity,那么就将这个栈中在目标Activity上面的Activity全部销毁,让目标Activity出现在栈顶,然后调用他的onNewIntent()方法

singleInstance

整体行为与singleTask类似,但是有以下区别:

  • 启动singleInstance的Activity的时候,不管新Activity和当前Task的taskAffinity是否一样,都会创建他独有的Task
  • 在singleInstance的Activity上启动其他Activity时,无论二者的taskAffinity是否一样,都不允许其他Activity进入当前task

singleTask的Activity只会单独存在在一个task中, 并且这个task中不能有其他的Activity,强调其独占

taskAffinity

taskAffinity代表的是task的相关性,可以认为是Android想要将同一个逻辑链条中的Activity放入统一个Task中,在Android中,不同task的taskAffinity是可以相同的

指定TaskAffinity

可以在AndroidManifest中通过android:taskAffinity属性指定Activity所在task的taskAffinity

taskAffinity

  • 如果Activity没有指定taskAffinity,那么就会使用application的taskAffinity
  • 如果Application没有指定taskAffinity, 那么就会使用应用的包名作为taskAffinity

最近任务

按下Android中的多任务键,会进入多任务页面,多任务页面中显示的实际上是所有的task,并不是应用,当有多个task具有相同的taskAffinity是,最近任务列表里只会显示最近新展示过的一个,所以如果标记了singleInstanse的Activity,完全有可能不显示在多任务页面中

例如:
AActivity标记为singleInstance,AActivity启动了本应用的BActivity,此时AActivity和BActivity就会分别处于不同的task栈中,但是这两个栈的taskAffinity都是应用的包名(默认情况下),此时点击多任务页面,只能看到BActivity的task,AActivity的task由于taskAffinity与BActivity的taskAffinity相同而被隐藏起来了

Task要不要在最近任务列表里显示

上述的显示规则,也可以被FLAG改变:

  • FLAG_ACTIVITY_NEW_DOCUMENT: 添加了这个Flag的Activity,每个都会独立显示在最近任务里,但是关闭后,不会在最近任务里保留残影
  • FLAG_ACTIVITY_RETAIN_IN_RECENTS: 可以修改上面的规则,让Activity在最近任务里保留残影
  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:这个Activity从一开始就不会显示在最近任务里,切走之后就再也切不回来了

返回键

当点击Android中的返回键时,Android会按照以下逻辑进行执行:

  1. 对当前task执行出栈的操作,显示出当前task的上一个Activity
  2. 如果当前task执行出栈后,没有其他Activity,则销毁这个task,但是并不会销毁其在多任务页面的快照
    1. 当前task销毁后,如果有其他前台task,则会切换到上一个前台task
    2. 当前task销毁后,如果没有其他前台task,则会直接显示Home

所谓的前台task,指的是再Activity运行过程中,使用startActivity的方式开启了其他的task,这个时候这些新启动的task就会以栈的形式组织起来,当一个task销毁后,就可以显示上一个task

但是,如果用户按下了home键,或者多任务键后, 这些以栈形式组织起来的task就会被打散,用户是无法通过返回键再回到上一个task中.

例如:

  1. 在便签应用中点击网址,此时会启动浏览器,这个时候前台的task有两个:便签App的task和浏览器的task,每个task中只有一个Activity此时如果按下返回键,会将浏览器的Task销毁,然后页面回到了便签App
  2. 在便签应用中点击网址,此时会启动浏览器,此时按下多任务键,然后再回到浏览器,此时,前台的task就只剩下浏览器App一个了,这个时候按下返回键,Android会销毁浏览器的Task,然后回到Home

更完整的场景分类

Activity的启动,可以有很多分类方式:

打开的时候放进哪个Task

  1. 默认情况下(standard)以及singleTop,是属于当前所属的逻辑链条,即直接放入当前的task,而不去检查taskAffinity

  2. 如果是singleTask,就需要判断当前Activity和目标Activity的taskAffinity是否相同,相同就会放在当前task,不同就放入别的Task,而且在放进别的Task的时候,也会先尝试找到一个和目标Activity的taskAffinity匹配的Task,找到就进入这个Task,找不到才会创建新的Task

    总结起来,singleTask其实是个很简单的规则: 查找taskAffinity相同的Task,找到就让Activity进入,找不到就创建新的Task

    而更本质上, 其实Activity启动的时候,所有的launchMode都会 穿换成对应的Intent的Flag(0个或多个都有可能),其中 singleTask会被转换成好几个Flag的叠加,其中一个就是 Intent.FLAG_ACTIVITY_NEW_TASK,如果在startActivity 的时候,在Intent里添加上这个Flag:

    Intent intent = new Intent(this,MainActivity.class) ;
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
    

    那么新Activity启动时的入栈规则就会和配置了singleTask的 Activity一样.

    但是我们实际上也可以跳过这个搜索过程,如果在Intent里除了 FLAG_ACTIVITY_NEW_TASK之外,再配置上 FLAG_ACTIVITY_MULTIPLE_TASK:

    Intent intent = new Intent(this,MainActivity.class) ;
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 
            Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
    startActivity(intent);
    

    那么Activity就会跳过搜索,直接创建一个新的Task来吧这个 Activity放在栈底

  3. 如果launchMode是singleInstance,并且目标Activity和当前的Activity不同,就放进新的单独的Task;

    • 另外,如果当前的Activity已经是singleInstance,那不管目标Activity是什么,只要二者不是同一个Activity,就会把目标Activity放进别的Task

打开的时候要不要清掉顶部的Activity

前面提到FLAG_ACTIVITY_NEW_TASK的入栈规则和singleTask一样,同时singleTask会被转换成多个Flag,而不只是FLAG_ACTIVITY_NEW_TASK,singleTask的行为规则除了入栈方面,还有清理顶部: 如果目标Activity已经在目标Task,但是不在栈顶,就会把上面的Activity全部清理掉.而这个清理顶部也是singleTask所对应的Flag之一:FLAG_ACTIVITY_CLEAR_TOP,需要将FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_CLEAR_TOP配合在一起使用,才能达到类似singleTask那样的清理顶部的效果,否则只有FLAG_ACTIVITY_CLEAR_TOP的话,Activity入栈时永远会在栈顶,就没有顶部可以清理了

打开时要不要复用已有的Activity

在singleTask的规则里, 如果目标Task里已经有了目标Activity,目标Activity顶部的其他Activiy会被清理掉,让activity回到栈顶;而Intent.FLAG_ACTIVITY_CLEAR_TOP做的也是清理顶部的活,看起来好像一样, 但是singleTask除了清理顶部,还会复用栈内的目标Activity,调用它的onNewIntent方法,这点和singleTop是一样的,而Intent.FLAG_ACTIVITY_CLEAR_TOP并不会复用Activity,他的默认规则是:将目标Activity顶部的其他Activity,以及目标Activity一起,全部清理掉,然后重新启动目标Activity到栈顶.

如果想在清理其他Activity的时候,别把目标Activity一起清掉,需要加上另一个flag:

FLAG_ACTIVITY_SINGLE_TOP

这个flag就是singleTop,所对应的唯一flag.

而singleTask其实对应了3个flag:FLAG_ACTIVITY_NEW_TASK+FLAG_ACTIVITY_CLEAR_TOP+FLAG_ACTIVITY_SINGLE_TOP,所以singleTask实际上就是这三个flag共同作用的规则:

  1. 让Activity寻找和自己taskAffinity相同的Task(FLAG_ACTIVITY_NEW_TASK)
  2. 清理掉目标Activity顶部的其他Activity(FLAG_ACTIVITY_CLEAR_TOP)
  3. 复用栈中已有的目标Activity(FLAG_ACTIVITY_SINGLE_TOP)

实战选择

  • standard和singleTop多用于App内部
  • singleInstance:多用于开放给外部App来共享使用
  • singleTask:内部交互和外部交互都会用得上
  • 启动Activiy尽量不要设置singleTask,因为这样用户每次点击图标的时候,都会将task中其他的Activiy全部杀死.即使配置了clearTackOnLaunch = false也不能解决

猜你喜欢

转载自blog.csdn.net/cfy137000/article/details/109470990