Android中LaunchMode详解

宝剑锋从磨砺出,梅花香自苦寒来。这篇文章主要讲述Android中LaunchMode详解相关的知识,希望能为你提供帮助。
越是做的时间越长, 基础知识就忘的越干净, 最近做一个项目中, 发现启动的几个Activity居然重叠了, 我ri~ ~ , 再不回忆一下就要退出android界了。
概念解释 Task Task叫做任务, 这个简单, 表示我们需要完成的事情, 注意, 这里我们说的是任务, 是个名词, 例如要发短信, 那我们的任务就是发送一条短信, 仅此而已, 再例如教官说: ”张三, 你去吃屎! ”, ok, 那张三的任务就是吃屎。
Back Stack 我们常叫做回退栈, 或者是任务栈, 这个是什么意思呢? 上面我们说过, 需要完成任务, 那我们就需要使用一系列的Activity来完成, 例如发短信, 则完成该任务需要如下步骤:

  • 打开短信主页面MainActivity
  • 点击添加短信按钮, 打开NewSMSActivity
  • 在NewSMSActivity中编写短信并发送
以上的任务中涉及到两个Activity, 那这两个Activity就存放在这个Back Stack中, 又因为Back Stack是栈类型的数据结构, 所以上面的步骤在这个Back Stack中的活动顺序如下:
  • MainActivity先压栈
  • 点击添加按钮, NewSMSActivity压栈,
  • 短信发送完成, 点击返回按钮, NewSMSActivity弹栈, 回到MainActivity
  • 在MainActivity点击返回按钮, MainActivity弹栈, 此时该Back Stack为空, 就返回到Launcher了
所以我们明白了, 这个回退栈其实就是一个存储Activity实例的容器, 执行每个Task时, 先创建一个Back Stack, 在Task执行过程中将所使用的Activity都按照FILO的顺序以此压入这个Back Stack, Task目标完成之后, 按下返回按钮时, Back Stack中的Activity按照压栈相反地的顺序以此弹栈, 直到栈中没有Activity实例时, 进入Launcher。
由此, 我们还可以知道, 每个Task和Back Stack是一一对应的关系, 一般情况下, 每需要执行一个Task时, 都至少需要一个Back Stack容器, 并且这个容器中都至少会有一个Activity实例。
LaunchMode的作用和使用方法 一般情况, 如果没精神病, LaunchMode是用在Activity上面的, 我们就谈谈在Activity上的LaunchMode
LaunchMode作用 顾名思义, LaunchMode就是启动模式, 啥是启动模式? 启动模式意思是使用不同的模式启动之后, 会有不同的属性和表现, 举个例子, 钢铁侠使用正常模式启动, 一般可以秒杀所有小兵小将, 但是如果对付发狂的绿巨人浩克, 就需要启动超强模式, 穿上反浩克装甲, 要不然打不过, 放到我们Actiivty这里也是一样, 既然是配置在Activity上的, 那就说明Activity有好几种启动模式, 使用不同的启动模式启动的Activity有不同的属性和表现。
那为啥需要启动模式呢? 需求! 对, 需求是所有东西被建立或者被制造出来的原因, 因为我们对Actiivty有不同的需求, 举个老生常谈的发邮件例子, 邮件主页Activity要求不论怎么打开, 打开多少次, 就只能有一个主页Activity的实例, 对吧, 如果有多个实例我们就很麻烦, 不知道显示哪个, 也不知道要关闭哪个, 那这就是个需求, 对应这个需求, 我们就需要对这个主页Activity设置一种启动模式, 不论怎么打开就只有一个实例, 这样需求就满足啦。
LaunchMode使用方法 按照Android Developer上的说明, LaunchMode有两个使用地方, 一个是Mainfest的activity节点下, 一个是在startActivity方法的Intent中设置Flag, 第二种方法我们后续再讨论, 先说说第一种方法。
要告诉小白的是, LaunchMode要在启动该Activity之前使用才有效, 如果Activity都已经启动了, 实例都创建完了, 再设置什么模式都没用啦。
第一种使用方式特别简单, 在Mainfest中的activity节点中添加android:launchMode即可, 如下所示:
< activity android:name= " .MainActivity" android:label= " 邮件列表" android:launchMode= " singleTask" > < /activity>

有四种launchMode可选, 分别是:
  • “standard”
  • “singleTop”
  • “singleTask”
  • “singleInstance”
如果不添加launchMode也可以, 默认的launchMode是”standard” 。
按照Android Developer官方上, 他们按照Activity是否可以被实例化多次, 把这四个模式分了两个组, “standard” 和”singleTop” 属于可以被实例化多次这个组, 他们的实例可以属于任何Task, 并且可以位于Back Stack的任何位置, 其余两个属于不可被实例化多次这组, 他们常用于启动一个Task, 所以一个Task只有一个这种实例, 并且这个还往往位于Back Stack的最开始。这种分组可以帮助我们初步理解每种启动模式的区别。
standard 从现在开始好好讲解这四种启动模式了, 首先看看”standard”, 有时称为标准模式。
前面我们说过, Activity默认就是这种模式的, 所以你的Activity设置和不设置这个没什么区别, 那这种模式的表现是什么样子的呢?
假设我们有一个”standard”模式的Activity, 页面上有个按钮, 点击这个按钮就会启动这个Activity自身, 由于设置的是”standard”模式, 每次启动这个Activity, 就会创建这个Activity的新的实例, 并依次放入Back Stack, 点击一百次就会创建一百个这个Activity的实例。
“standard”是最简单的模式, 也符合我们正常的思维逻辑, 所以最好理解, 我用简陋的画图工具画了个图, 凑合着看吧:
Android中LaunchMode详解

文章图片

singleTop 这种启动模式和标准模式区别不大, 只有一点点不同。
我们已经知道, 每个Activity的实例在Back Stack中存储, 既然是个Stack数据结构, 那么第一个压栈的实例我们叫做栈底实例, 因为它将被后进来的实例压在最下面, 最后被压入的实例, 称作栈顶实例, 因为它刚被压人栈中, 暂时还没有其他实例在它之上, 如果栈中只有一个实例, 那这个实例既是栈底实例, 也是栈顶实例。
明白了栈底和栈顶的概念, ”singleTop” 就好理解了, 当我们启动”singleTop” 模式的Activity时, 系统会检查当前的Back Stack的栈顶实例是不是这个”singleTop” 模式Activity的实例, 如果是的话, 就不创建新的实例了, 直接复用这个已经存在的栈顶实例。还拿我们上一个场景为例, 如果这个Activity是”singleTop” 模式的, 不论你怎么点按钮, Back Stack只会有一个实例, 因为栈顶已经存在一个这样的实例, 所以不会创建新的了。
如果上个例子不够明确, 我们可以举个比较明显的例子, 有两个Activity: ActivityA是标准模式, AvtivityB是”singleTop” 模式, 完成一个Task, 需要经过以下步骤:
  • 启动ActivityA, 在ActivityA中点击按钮启动ActivityB,
  • 在ActiivtyB中点击按钮, 再次启动ActivityB
根据之前的描述, 当ActivityB的实例第一次被创建时, 是位于栈顶的, 第二次尝试创建ActivityB的实例之前, 由于是”singleTop” 模式, 并且栈顶已经有它的实例, 就不会再创建新的, 这个Task完成之后, Back Stack中只有一个ActivityA的实例和一个ActivityB的实例, 简图如下:
Android中LaunchMode详解

文章图片

singleTask 【Android中LaunchMode详解】接下来是”singleTask” 模式, 还记得文章开头说的两个分组和区别吗? 如果你现在还记得, 那你就理解了一大半了。
之前说过, ”singleTask” 的Activity在一个Back Stack只会创建一个, 这是和前两个模式最大的不同, ”singleTask” 和”singleTop”不同之处在于, 创建实例时不仅仅只检查栈顶是否已经有实例, 还会检查整个Back Stack, 只要Back Stack已经存在实例, 不论是位于栈顶, 栈底还是哪里, 都不会创建新的实例。
“singleTask” 模式不仅不会创建新的实例, 还会将从已经创建实例开始到栈顶的所有实例全部清除, 并将已经创建的实例放到栈顶, 因为只有这样做才会让这个实例可以显示到窗口上。
还是继续上面的例子, ActivityC的启动模式为”singleTask” , 我们在Back Stack中原来的ActivityA和ActivityB实例之间插入一个ActivityC的实例, 形成这样的Back Stack结构之后, 如果再次尝试启动ActivityC时, 由于ActivityC的实例已经存在, 所以复用已经存在的实例, 并且清除实例到栈顶的所有实例, 所以ActivityB的实例被清除了, 此时Back Stack中就只有ActivtyA和ActivtyC的实例。
Android中LaunchMode详解

文章图片

singleInstance “singleInstance”是最后一个启动模式, 则是和其他三个模式都不同的。
我们之前谈论的启动模式, 基本都是在一个Back Stack范围内谈论是否需要重新创建的问题, 在这里, 我们把范围扩大一下, 讨论在多个Back Stack之间重新创建的问题, 设置了”singleInstance”模式的Activty, 在启动的时候, 会脱离于当前Task的Back Stack, 在一个新的Back Stack中创建实例。
还是我们之前的例子, 现在把ActivtyA, ActivityB和ActivtyC的实例依次放入Back Stack中, 并将这个Back Stack编为1号, 然后编写一个ActivityD并将其设置为”singleInstance”模式, 此时, 如果我们在ActivityC中启动ActivityD, 那么ActiviyD的实例将不会位于1号Back Stack中, 它将会在一个新的Back Stack中创建一个新的ActivityD实例, 简图如下:
Android中LaunchMode详解

文章图片

使用场景 不同的启动模式, 适用与应用中的不同应用场景。
standard 标准模式适用于大多数场景, 因为在应用中, 我们基本上可以允许用户同时进行多个任务, 每个任务操作不同的数据, 这样允许创建一个Activty的多个实例, 例如新建邮件Activity, 如果当前正在新建一个给张三的邮件, 此时同时需要创建一个给李四的邮件, 此时Activty就要使用标准模式, 这样允许创建多个不同的实例, 允许创建多封邮件。
singleTop singleTop模式, 由于其特点是检查栈顶实例, 可以用这个特性, 防止短时间创建多个实例, 例如有个按钮, 点击之后打开一个播放视频的Activty, 如果用户短时间重复点击, 不是singleTop模式的话, 就会短时间在Back Stack中出现多个实例, 而且每个实例的播放进度不一致, 如果是singleTop模式的话, 不论打开多少次都没有影响。
singleTask 这个模式, 常用于那些有一定任务, 且任务已经进行了一部分, 但是忽然又去做别的事情了, 等会儿回来还要继续任务的场景, 还是播放那个视频的例子, 如果正在播放的时候需要去打开新Activty搜索相关视频, 搜索完毕又要回到播放页面, 那这个页面就比较适合使用singleTask模式
singleInstance 这种模式较少使用, 如果一定要在你的应用中使用这种启动模式, 请提前考虑好是否有这个必要, 另外, 在做一些特殊场景下的应用, 比如Launcher的主屏时, 可能会使用得到。

    推荐阅读