全面解析Activity(二): Activity的启动模式

Activity作为四大组件之首,使用极其频繁。有时问了项目特殊需求,就必须用到Activity的启动模式。

Activity的启动模式也是难点之一,主要难点在于形形色色的启动模式和标志符。本文将进行全面的学习。

首先说一下Activity为什么需要启动模式。我们知道,在默认的情况下,当我们多次启动同一个Activity的时候,系统会创建多个实例并把他们一一放入任务栈中,当我们点击back键的时候会发现这些Activity会一一回退,任务栈是一种"后进先出"的栈结构,这个好理解, 每按一次back键就有一个Activity退出栈,直到栈空为止,当这个栈无任何Activity的时候,系统就会回收这个任务栈。知道了Activity的启动模式,我们可能就会发现一个问题:多次启动同一个Activity会创建多个实例,这样是不是很傻,谷歌工程师当然也考虑到这个问题,所以设计Activity的时候提供了启动模式来修改系统的默认行为。

(一)AndroidManifest中配置

Android开发者可以在AndroidManifest文件中一共设计了四种启动模式:standard、singleTop、singleTask、singleInstance。我们结合例子一一进行说明(例子源码见文末)。

1.standard(标准模式)

默认的启动模式,如果不指定Activity的启动模式,则使用这种模式来启动Activity,每次点击standard模式创建Activity之后,都会创建新的MainActivity覆盖在原有的Activity上。

例如,若我有一个Activity名为A1, 上面有一个按钮可跳转到A1。那么如果我点击按钮,便会新启一个Activity A1叠在刚才的A1之上,再点击,又会再新启一个在它之上……点back键会依照栈顺序依次退出。

2.singleTop(栈顶复用模式)

如果指定Activity的启动方式为singletop,那么在启动的时候,系统会判断当前栈顶Activity是不是要启动的那个,如果是则不创建新的Activity,如果不是则创建新的Activity,这种模式通常适用于接收到消息后显示的界面,列入QQ接收到消息后弹出Activity,如果一次来10条,总不能弹10次吧。

这种启动模式虽然不能创建新的实例,但是系统任然会在Activity启动的时候调用onNewIntent()方法。

例如:若我有两个Activity名为B1,B2,两个Activity内容功能完全相同,都有两个按钮可以跳到B1或者B2,唯一不同的是B1为standard,B2为singleTop。
若我意图打开的顺序为B1->B2->B2,则实际打开的顺序为B1->B2(后一次意图打开B2,实际只调用了前一个的onNewIntent方法)
若我意图打开的顺序为B1->B2->B1->B2,则实际打开的顺序与意图的一致,为B1->B2->B1->B2。

3.singleTask(栈内复用模式)

singleTask模式和singleTop模式有点类似,只不过singleTop是检测栈顶元素是否需要启动的Activity,而singleTask是检测整个Activity栈中是否存在当前启动的Activity,如果存在,就将他置于栈顶,并且将以上的activity全部销毁,不过这里也是指在同一个App中启动整个singleTask的Activity,如果是其他的程序以singleTask模式来启动整个Activity,那么他将创建一个新的任务栈,不过这里有一点需要注意的是,如果启动的模式为singleTask的Activity已经在后台的一个栈中,那么启动后,后台的一个任务栈将一起被切换到前台,借助官网的一张图我们可能更好的理解。

这里写图片描述

当Activity2启动ActivityY的时候(启动模式为singleTask),它所在的task被切换到前台,且按返回键返回的时候,也会先返回ActivityY所在Task的Activity,这一点比较难理解,大家根据图去研究一下。

可以发现,使用这个模式创建的Activity不是在新的任务栈中被打开,就是将已打开的Activity换到前台, 所以这种启动模式通常可以用来退出整个应用,将主Activity设为singlelask模式,然后在要退出的AcnVity中转到主Activity,从而将主Activity之上的AcnVity全部销毁,然后重写主Activity的onNewIntent方法 在方法中加上一句finish,将最后一个Activity结束掉。

听完后,是不是依旧云里雾里。我们还是通过demo中的例子来说明吧:

若我的应用程序中有三个Activity,C1,C2,C3,三个Activity可互相启动,其中C2为singleTask模式,那么,无论我在这个程序中如何点击启动,如:C1->C2->C3->C2->C3->C1-C2,C1,C3可能存在多个实例,但是C2只会存在一个,并且这三个Activity都在同一个task里面。

但是C1->C2->C3->C2->C3->C1-C2,这样的操作过程实际应该是如下这样的,因为singleTask会把task中在其之上的其它Activity destory掉。

操作实际说明
C1->C2C1->C2
C1->C2->C3C1->C2->C3
C1->C2->C3->C2C1->C2C3在C2上
C1->C2->C3->C2->C3->C1C1->C2->C3->C1C3在C2上,C1在C2下
C1->C2->C3->C2->C3->C1-C2C1->C2第二次的C1在C2下

若是别的应用程序打开C2,则会新启一个task。

如别的应用Other中有一个activity,taskId为200,从它打开C2,则C2的taskIdI不会为200,例如C2的taskId为201,那么再从C2打开C1、C3,则C2、C3的taskId仍为201。

注意:如果此时你点击home键,然后再打开Other,发现这时显示的肯定会是Other应用中的内容,而不会是我们应用中的C1 C2 C3中的其中一个。

4.singleInstance(单实例模式)

singieInstance这种启动模式和使用的浏览器工作原理类似。在多个程序中访问浏览器时,如果当前浏览器没有打开则打开浏览器, 否则会在当前打开的浏览器中访问. 申明为singleInstance的Activity会出现在一个新的任务栈中而且该任务栈中只存在这一个Activity,举个例子来说,如果应用A的任务栈中创建了MainActivity实例,且启动模式为singleInstance,如果应用B也要激活MainActivity 则不需要创建,两个应用共享该Activity实例,这种启动模式常用于需要与程序分离的界面: 如在setupWizard中调用紧急呼叫,就是使用这种启动模式。

程序有三个ActivityD1,D2,D3,三个Activity可互相启动,其中D2为singleInstance模式。那么程序从D1开始运行,假设D1的taskId为200,那么从D1启动D2时,D2会新启动一个task,即D2与D1不在一个task中运行。假设D2的taskId为201,再从D2启动D3时,D3的taskId为200,也就是说它被压到了D1启动的任务栈中。若是在别的应用程序打开D2,假设Other的taskId为200,打开D2,D2会新建一个task运行,假设它的taskId为201,那么如果这时再从D2启动D1或者D3,则又会再创建一个task,因此,若操作步骤为other->D2->D1,这过程就涉及到了3个task了。

关于singleTop和singleInstance这两种启动模式还有一点需要特殊说明: 如果在一个singleTop或者singleInstance的Activity中通过startActivityForResult()方法来启动另一个ActivityB, 那么系统将直接返回Activity_RESULT_CANCELED而不会再去等待返回。 这是由于系统在framework层做了对这两种启动模式的限制, 因为Android开发者认为,不同的Task中,默认是不能传递数据的。如果一定要传递数据的话,那么只能通过Intent去绑定数据。

(二)Intent Flag启动模式

前面就已经说了,系统提供了两种方式来设置一个Activity的启动模式,下面要讲的是通过intent设置flag来设置一个Activity的启动模式。

下面列举一下常用的Flag。

  • Intent.FLAG-ACTIVITY-NEW-TASK
    使用一个新的Task来启动一个Activity,但启动的每个Activity都在新的Task栈中。该Flag通常使用在从service中启动的Activity场景,由于Service中并不存在Activity栈,所以用该Flag来创建一个新的Activity栈,并创建新的Activity实例。

  • FLAG-ACTIVITY-SINGLE-TOP
    使用singletop模式来启动一个Activity,与指定android:launchMode=”singleTop”效果相同。

  • FLAG-ACTIVITY-CLEAR-TOP
    使用SingleTask模式来启动一个Activity,与指定android:launchMode=”singleTask”效果相同。

  • FLAG-ACTIVITY-NO-HISTORY
    使用这种模式启动Activity,当该Activity启动其他Activity后,该Activity就消失了,不会保留在Activity栈中,例如A-B,B中以这种模式启动C,C再启动D,则当前Activity栈为ABD。

(三)补充说明

上述两个方式都可以为Activity指定启动模式,但是两者还是有区别的。首先,**优先级上,第二种方法(Intent Flag)的优先级要高于第一种(AndroidManifest),当两种同时存在时,以第二种方式为准;**其次,上述两个方式在限定范围上有所不同,比如,第一种方法无法直接为Activity设定FLAG_ACTIVITY_CLEAR_TOP标识,而第二种无法为Activity指定singleInstance模式。


源码下载

点我下载


参考

  1. 官方开发者文档: 点我跳转
  2. 《Android编程权威指南(第3版)》
  3. 《Android群英传》
  4. 《Android开发艺术探究》

关于我

  1. 一个热爱Android,也喜欢交友的菜鸟. QQ:984992087
  2. github链接: 点我跳转
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值