Activity 的 Task 以及 launchMode 研究

android 的 Task 和 Back Stack

Task 简介

android 中的Task是一系列Activity的集合,用户通过操作多个Activity来完成一个Task。一个Task的具体实现就是一个包含多个Activity的栈,当用户进入某一个app的时候,app的入口Activity被压入栈底,用户每进入一个Activity,该Activity都会被添加到该app的栈顶。Task中的每个Activity的顺序是不会重新排列的,Task的动作只有进栈和出栈。在android中点击OverView Screen的按钮可以进到OverView Screen的界面,如图:

OverView Screen图片

图中展示出了现在系统中所有的Task,最前面的是前台的Task,其他的是后台的Task。

更多的关于Task的信息请参考:官方文档

Back Stack 回退栈简介

回退栈指的是当用户点击返回键的时候,android系统会一个接一个地pop出Task中所保留的Activity。回退栈更多地是表示逻辑上的一个概念,可以理解成Task的逻辑抽象。

Task 和 Back Stack的关系

Task 可以理解成 Back Stack的一种具体实现。除了包括Activity的Task之外,android系统中的Fragment也是具有回退栈的,但是Fragment并没有像Activity的Task那样的东西。

Task 与 Activity

Activity 的实例化是依赖于Task的,它必须被加载到某一个Task中才可以,从另一个角度来看,Task就是Activity的容器。android 系统在启动一个Activity的时候,会在系统中寻找是否存在符合该Activity 的自身属性 和 启动该Activity的Intent的要求的Task(具体怎么寻找,会在下面讨论),有的话直接在在该Task中实例化一个Activity,没有的话则需要新建一个Task,再在其中对Activity进行实例化。

系统如何给一个Activity寻找它的归属Task?

Task 有一个关键的属性affinity(中文为亲缘、近缘等,但是感觉译成中文并不合适,遂用英文直接阐述了)。android中每一个Activity也都有一个叫做affinity的属性,Activity的affinity值可以在mainfest配置文件中通过标签的andrid:taskAffinity属性指定,如果没有指定的话则默认使用该Activity所在app的包名。每一个Task的affinity属性值等于该Task的root Activity 的affinity值。具有相同affnity的Activity在被系统实例化的时候更加容易被驾到同一个Task中。当然,这只是比较关键点额一个点,android系统在给Activity找Task的时候会受到非常多因素的影响,有来自Intent实例的众多flag,还有来自mainfest配置文件中Activity的众多配置信息,这是一个复杂的过程,本文不进行深入讨论。

launchMode 研究

launchMode简介

launchMode 是Activity类的一个属性,该属性包括4个具体值:standard、singleTop、singleTask、singleInstance。我们都知道,android系统中启动Activity是通过Intent实例进行的,当系统收到一个Intent的实例需要去启动指定的Activity的时候,android系统会根据目标Activity的该属性值来决定是要要创建新的该Activity实例以及如何在Task中创建该Activity的实例。这就是launchMode这个属性的作用。

launchMode 分为两个类别

  • 普通类型:用户常用的启动模式类型,大部分Activity都是这两种启动模式。包括standdard 和 singleTop 两种启动模式
  • 特殊类型:具有特殊的行为的启动模式,只针对特殊需求的使用。包括 singleTask 和 singleInstance 两种启动模式

官方文档对4中启动模式描述如下:

使用场景 启动模式 是否可以有多个实例? 简介
普通类型 standard 系统默认的启动模式,当系统接收到一个Intent实例去启动一个standard模式的Activity时,系统总是会在目标栈的顶部创建一个新的activity实例,并把Intent的实例传进去。
普通类型 singleTop 视具体情况 当系统接收到一个Intent实例去启动一个singleTop模式的Activity时,如果在目标栈的顶部存在一个该Activity的实例的话,那么系统就会重用这个Activity的实例而不创建新的实例,并回调该Activity的onNewIntent(Intent intent)方法把新的Intent实例当作方法参数传递进去;如果在目标栈的顶部没有该Activity的实例的话系统将会在新建一个Activity实例,与standard的行为就一样了。
特殊类型 singleTask 当系统接收到一个Intent实例去启动一个singleTask模式的Activity时,如果不存在该Activity的实例的话,系统会先创建一个新的Task,并在该Task底部里面创建一个该Activity的实例,随后把Intent实例传递进去;如果已经存在一个该Activity的实例的话,系统就不会再创建新的实例,那么系统就会重用这个Activity的实例而不创建新的实例,并回调该Activity的onNewIntent(Intent intent)方法把新的Intent实例当作方法参数传递进去,同时,该Activity实例所在的Task将会被调到前台
特殊类型 singleInstance 类似于singleTask,唯一不同的地方在于,singleInstance的Activity不允许自己的Task中存在其他的Activity实例,也就是说singleInstance的Activity永远是Task中唯一的一个Activity实例

launchMode 的定义

  • 静态定义:在目标Activity的mainfest中使用标签静态定义
  • 动态定义:在启动目标Activity的Intent对象中使用不同的flag定义,可以用来定义launchMode的flag有以下三个:
flag 对应的launchMode
FLAG_ACTIVITY_NEW_TASK singleTask
FLAG_ACTIVITY_SINGLE_TOP singleTop
FLAG_ACTIVITY_CLEAR_TOP 无对应的launchMode

note:在Activity A启动 Activity B的时候,假如B已经在mainfest文件中定义过launchMode,此时的Intent对象中也有定义launchMode,这时候以Activity A start B的时候以Intent中定义的为准。

典型使用场景

launchMode 使用场景
standard 绝大多数标准的跳转
singleTop 新闻阅读类的内容页面,假如在一个新闻页面又打开了另一个新闻页面,这时候Task的顶端已经有该Activity的实例了,就不用再初始化了
singleTask 通知栏启动其他app,一般都会在Intent中加上FLAG_ACTIVITY_NEW_TASK,相当于使用了singleTask

一些会影响 Task 行为的 mainfest中activity的属性

属性名称 可取值 意义
android:taskAffinity 一个存在的包名 表示该Activity与该包名具有亲缘,在该Activity启动的时候,会优先选择被添加进已经存在的具有亲缘关系的Task中。
android:allowTaskReparenting true, false 如果一个Activity的该属性值是true,它先被一个外部app启动了,此时它存在于那个调用它的app的Task中,当与它有亲缘关系的Task被后台带回到前台的时候,系统会把它换到该Task中;如果值为false的话,就不会发生移动。
android:alwaysRetainTaskState true,false 正常情况下,当一个Task在后台待了很久之后,系统会把该Task的非root Activity都清除,只保留root Activity。但是假如root Activity的该属性值被设置为true的话,系统会保留该Task中所有的Activity。

android中Activity的launchMode总结

  • launchMode的作用,是让开发者可以在一定程度上决定自己的Activity该如何被系统实例化,从而来满足开发者的开发需求
  • launchMode一共分为两类,一类是普通开发者最常用的:standard 和 singleTop,其中standard是官方定义的最常见的也是默认的系统对Activity进行实例化的行为模式,singleTop在standard的基础上进行了点小的改动,当目标栈的顶部有Activity的实例的时候将不再对该Activity进行初始化而是直接使用现成的Activity实例,因为有很多的使用场景是standard不太合适而singleTop比较合适的;另一个类别的是普通开发者不常使用的,singleTask 和 singleInstance,官方并不推荐在通常的场景中使用该类的launchMode,只有在极少数特殊的情形中才去使用,该类launchMode的特点是Activity在系统中只会有一个实例存在,不同的地方在于singleInstance的Activity会独占一个Task,而singleTask的则不会。
  • launchMode可以在mainfest文件中静态地设置,同时,在代码中,通过Intent的几个flag也可以达到设置launchMode的目的。假如某一次启动一个Activity的时候,在Intent中指定了一个launchMode,而且该Activity本身已经在mainfest文件中设置了launchMode,那么这个时候系统会以Intent中指定的为准。android系统这样设计的目的,我想是为了让整个系统的Activity可以更方便地让其它Activity(可以不再一个app内)进行调用,这与android的开放的态度是一致的。

参考资料:
https://developer.android.com/guide/components/tasks-and-back-stack.html

https://developer.android.com/guide/topics/manifest/activity-element.html#aff

http://droidyue.com/blog/2015/08/16/dive-into-android-activity-launchmode/index.html

http://www.it610.com/article/5751414.htm

坚持原创技术分享,您的支持将鼓励我继续创作!