Android开发学习之路——Activity Task和Back Stack分析
一、概念
(1)首先需要理解Task和Back Stack的含义:
Task,翻译过来就是任务,指的是Activity的集合或者容器,而这个集合/容器呢,则是使用一个栈来进行管理的,这个栈称为Back Stack,栈中Acitvity的顺序则是遵循后进先出的原则,而栈中Activity的顺序是按照他们被打开的顺序来依次存放的。一般,我们只能从栈顶移除或者添加新的Activity,而栈中你的Activity的顺序是固定的,但是也有一些方法可以改变栈内的Activity的顺序,也就是后面要介绍的launchMode和Intent的Flags。
总结:Task(任务) Activity的容器或者集合,通过Back栈来管理。而栈中Activity的顺序是按照启动Activity的先后来决定的,但是我们可以通过launchMode和Flags来改变顺序。
(2)为什么会选择Task和 Back Stack来管理Activity而不是其他的数据结构呢?(这里就需要分析Task和Back Stack的特性)
实际上一个应用包含很多个Activity,而每个Activity都对应了特定的功能,用于给用户操作,而不同的Acitivity可以通过Intent来互相启动。比如说从点击一个app图标开始,首先是启动的Activity,然后主界面Activity,然后点解头像,开始注册Activity,然后登录Activity。如下图 1:
图 1
在这一过程中该应用一共产生了四个Activity实例,那么如果此时我按返回,应该依次移除上层的Activity直到退出。这里思考,如果是你,你首先想到的是什么数据结构来保存这些?(如果学过数据结构的话,你会很敏感的知道这个很符合栈的结构特点)
除此意外,一个Acitivty还可以去启动其他应用程序中的Acitivity。比如,在微信聊天中,如果对方给你发一个网址链接,你点击后会在微信打开这个链接,但是这个时候你选择从浏览器打开,就会进入浏览器,当你看完这个链接后按返回退出浏览器,此时你会发现又会回到微信界面中。
因此无论是同一个应用的Acitivity还是不同应用的Activity,Android都提供了一种Activity管理将他们有效合理的结合在一起。而Task和Back Stack刚好可以满足这一需求。
那么Task和Back Stack是如何管理Activity的呢?
同样我们以图 1为例,首先是应用的启动Activity,这个Activity通常是一张图或者广告,如果是第一次启动的话,那么此时应用对应的Task不存在,所以,首先系统为该应用创建一个Task A,并且将启动Activity加入到Task的栈中,如图 2.
图 2
接下来一次点击启动其他Activity。如下图 3。
图 3
可以看到,主Activity启动注册Activity后,注册Activity加入Task,并处于back stack的栈顶。此时剩下的三个Activity都处于停止状态,并且系统会保存他们的相关信息,比如说滚动位置文本框的输入内容等。
如果此时按返回键,则从栈顶移除登录Activity,注册Activity成为栈顶元素。
前面讲了同一个应用的Activity的跳转,那么不同的应用(跨进程)之间启动Activity又会是什么情形?
这里假设应用A的A Activity启动了应用B 的B Activity,则可能会出现两种情况,一种是应用B 的B Activity加入到A 的Task中并位于栈顶,另一种则是B 的B Activity会在一个新创建的Task或者是已存在的另一个Task中。具体的后面根据LaunchMode和Flags来具体分析。
如果用于按返回键,则栈顶的Acitivity会移除,被移除的这个Activity将会被销毁。之前的一个Activity处于栈顶并获得焦点。当Back Stack中所有的Activity都移除的话,此时对应的Task也就不存在了。
一个新的应用启动时,其Task会移动到前台。而此时如果按Home键或者启动另一个应用,则该Task处于后台状态,并且Task中所有的Activity都处于停止状态。而用户可以不停的在不同的应用之间做切换,此时对应的Task则在前台和后台之间转换,这也就是常说的Android多任务切换的例子。
(3)既然新启动的Activity会加入到栈顶,那么是否会有这样一种情况,如果重复启动同一个Activity,那么会创建多个该Activity的实例,并且加入到同一个Task中,这样一个BackStack就会有很多重复的实例。例如下图4:
图 4
如果是上图的情形,你按返回,将会发现,按四次返回才可以回到主Activity,很明显这是一个很严重的问题。
那么实际开发中我们如何去解决这个问题呢?接下来就为大家介绍这几种解决办法。
二、Activity的启动模式(LaunchMode)(这里需要针对是启动同一应用还是不同应用的Activity以及不同应用的情况下是否会创建新的Task)
(1)standard(默认启动模式)
即每次启动一个Activity都会创建一个新的实例,然后加入到Task中。
同一应用这里不需要赘述,这里主要针对跨进程的情形,即启动的是另一个应用的Activity,在Android5.0以前,被跨进程启动的Activity,会加入到启动它的应用的Task中;而在Android5.0之后,会创建一个新的Task,然后加入。
(2)singleTop(栈顶复用模式)
即每次启动一个Activity会判断当前Task栈顶是不是该Activity的实例,如果是,则调用onNewIntent()重新启动,否则创建新的实例并加入Task。
这里跨进程的情形同standard。(会创建新的实例,但Android5.0前后是不同的)
(3)singleTask(栈内复用模式)
即如果该栈中有其实例(没有栈顶的限制条件),则不会重新创建实例,而是将之前的Activity移除,将其置于栈顶。
这里具体分析跨进程启动的问题,假设在该启动模式下,跨进程启动Activity A,如果被启动的应用A不存在Task A,则会先创建Task A;如果存在Task A,但没有该Activity A 的实例,则将A加入D到Task A中。如果存在Activity A实例,则将A上面的Activity移除,使其位于栈顶。
1>启动同一个应用的Activity
如果当前Task中存在该Activity的实例,并且不在栈顶,则会将该Activity上的所有Activity都移出栈顶,使该Activity在栈顶,并且调用它的onNewIntent()方法。
这里假设当前应用的Task为Task 1,目前已有的Activity实例为A,B,C
* 此时C启动该应用中的Activity D,则如下:
* 此时C启动该应用中的Activity A,则如下:
2>启动跨进程Activity
对于跨进程的Activity,加入该Activty和目标Task都不存在,则会先创建Task,在创建Activity并位于栈底。如果目标Task存在,且已经存在了将要启动的Activity的实例,则会将该Activity上的Activity都清除,然后调用onNewIntent()方法。如下:
(4)SingleInstance(单一实例模式)
即整个手机系统里只有一个实例存在。(不论在哪个Task中)
那么它是如何实现是有一个实例的?
对于声明为SingleInstance的Activity,它所在的栈中不会再放入Activity,也就是这个Task中只有它一个实例。而通过它启动的Activity也会放入其他Task中,因此可以保持单一实例。
2、Intent跳转的Flags
除了Manifes中,还可以在startActivity的时候为iIntent添加一个flag来改变Activity与Task的关联方式。这里介绍集中常用的Flags。
- FLAG_ACTIVITY_NEW_TASK
这里注意它不同与Sing了Instance单一实例模式
singleInstance会在Activity不存在时,新建一个Task并放入,但是再次启动时就不会重复创建,而是保持单一实例状态。
NEW_TASK在每次启动时不论是否已经存在实例,都将该Activity放入一个新建的Task
- FLAG_ACITVITY-SINGLE_TOP
- FLAG_ACTIVITY_CLEAR_TOP
3、清空返回栈
默认情况下, 一个Task在后台过了很长时间后,系统会将该Task中除最底层的Activity清除掉,在重新回到前台后,最底层的Activity会得到恢复。
而Android提供了方法,可以改变这种默认的状态,即在Manifest中的<activity>属性中进行修改。
(1)alwaysRetainTaskState
翻译过来就是总是保持Task状态,顾名思义,设置为True后,即使Task后台一段时间该Task仍然会保留。
(2)clearTaskOnLaunch
翻译过来在启动时清理Task,准确来说,如果把最底层的Activity的clearTaskOnLaunch属性设置为true,则在该Task进入后台后,会立即清理该Activity以上的所有Activity,这一点和 alwaysRetainTaskState。
(3)finishOnTaskLaunch
如果某一个Activity设置了该属性,则在该Task处于后台后,这个Activity就会被清除。和前两个属性相比,finishOnTaskLaunch是作用与Activity。