Android堆栈分析

279 阅读7分钟

在开发中,与界面跳转联系比较紧密的概念是Task(任务)和Back Stack(回退栈)。activity的启动模式会影响Task和Back Stack的状态, 进而影响用户体验。除了启动模式之外,Intent类中定义的一些标志(以FLAG_ACTIVITY_开头)也会影响Task和Back Stack的状态。 在这篇文章中主要对android的堆栈管理进行分析和验证,其中涉及到activity的一个重要属性taskAffinity和Intent中的标志之一FLAG_ACTIVITY_NEW_TASK。

Task和BackStack简介

1、task(任务)是一组Activities的集合,一组Activities被Stack(back stack)所管理,栈中Activity的顺序就是按照它们被打开的顺序依次存放的。

2、手机的Home界面是大多数task开始的地方,当用户在Home界面上点击了一个应用的图标时,这个应用的task就会被转移到前台。 如果这个应用目前并没有任何一个任务的话(说明这个应用最近没有被启动过),系统就会去创建一个新的task, 并且将该应用的主Activity放入到返回栈当中。

3、当一个Activity启动了另外一个Activity的时候,新的Activity就会被放置到返回栈的栈顶并将获得焦点。 前一个Activity仍然保留在返回栈当中,但会处于停止状态。当用户按下Back键的时候,栈中最顶端的Activity会被移除掉, 然后前一个Activity则会得重新回到最顶端的位置。返回栈中的Activity的顺序永远都不会发生改变, 我们只能向栈顶添加Activity,或者将栈顶的Activity移除掉。因此,返回栈是一个典型的后进先出(last in, first out)的数据结构。

4、task是可以跨应用的,这正是task存在的一个重要原因。有的Activity,虽然不在同一个app中,但为了保持用户操作的连贯性, 把他们放在同一个任务中。例如,在我们的应用中的一个Activity A中点击发送邮件,会启动邮件程序的一个Activity B来发送邮件, 这两个activity是存在于不同app中的,但是被系统放在一个任务中,这样当发送完邮件后,用户按back键返回,可以返回到原来的Activity A中, 这样就确保了用户体验。

Task和Stack的实际应用

1、使用Manifest文件

当在manifest文件中定义Activity的时候,你可以通过元素的launchMode属性来指定这个Activity应该如何与任务进行关联。 launchMode属性一共有以下四 种可选参数:

  • standard:standard是标准启动模式,也是默认启动模式,这种情况下,不管当前 Back Stack(回退栈)中有没有要启动的Activity实例,系统 都会将一个  新的Activity实例放在栈顶。

  • singleTop:如果当前Back Stack栈顶是要启动的Activity,那么就不会再创建 一个 Activity实例,而是直接显示当前栈顶的Activity,同时调用该Activity的onNewIntent() 方法;否则就会创建一个新的Activity实例放在栈顶。

  • singleTask:当一个Activity启动模式设置为singleTask时,如果要启ActivityA,但  是当前Back Stack中已经存在ActivityA只是不在栈顶,这个时候会将 ActivityA上面的Activity全部移除,使得ActivityA处于栈顶,同时也会调用ActivityA的onNewIntent方 法。 

  • singleInstance:他和singleTask类似,不同点是singleInstance要求activity的实例不仅只有一个,并且整个task中只有一个activity实例,而singleTask所在的栈中允许存在其他activity的实例。由此我们可以知道设置了singleInstance的activity也具有上面的特性,此外,启动的activity设置了singleInstance,那么无论被启动的activity有没 有设置singleInstance,都无法获取返回值。

2、使用Intent Flags

除了使用manifest文件之外,你也可以在调用startActivity()方法的时候,为Intent加入一个flag来改变Activity与任务的关联方式, 下面我们来一一讲解一下每种flag的作用: 

FLAG_ACTIVITY_NEW_TASK:

  • 如果传递给startActivity()的Intent对象包含FLAG_ACTIVITY_NEW_TASK标记,系统会为新Activity安排另外一个任务。还有一种情况,如果activity有存在着和当前activity相同affinity(亲和力)的任务,那么该acitvity就会压入那个任务里,至于affinity是一个什么东西,下面会仔细讲解。

FLAG_ACTIVITY_SINGLE_TOP:

  • 如果要启动的Activity在当前任务中已经存在了,并且还处于栈顶的位置,那么       就不会再次创建这个Activity的实例, 而是直接调用它的onNewIntent()方法。

FLAG_ACTIVITY_CLEAR_TOP:

  • 设置了这个flag,如果要启动的Activity在当前任务中已经存在了,就不会再次创建这个Activity的实例, 而是会把这个Activity之上的所有Activity全部关闭掉。这个时候有一点需要注意一下,比如说A activity设置了这个flag,此时跳转到B activity如果B activity没有设置,那么B activity就会被finish掉重新创建,反之就会调用OnNewIntent方法。

Task属性详解

  • affinity(亲和力):一个程序内/任务栈中的Activity具有亲和力,也就是说具有相同亲和力的Activity默认属于同一个任务Task中,taskAffinity属性接收一个字符串参数,你可以指定成任意的值(字符串中至少要包含一个.),但必须不能和应用程序的包名相同,因为系统会使用包名来作为默认的affinity值。

  • affinity有两种用法,第一个就是activity重新选择宿主,从一个Task 跳转到另一个Task里,那么新的Task就会成为新的宿主,还有一个就是 刚刚说到的 一个Flag,当设置了FLAG_ACTIVITY_NEW_TASK这个标记 时,activity跳转就会以这个Task为宿主

  • allowTaskReparenting:当把Activity的allowTaskReparenting属性设置成true时,Activity就拥有了一个转移所在任务的能力。 具体点来说,就是一个Activity现在是处于某个任务当中的,但是它与另外一个任务具有相同的affinity值, 那么当另外这个任务切换到前台的时候,该Activity就可以转移到现在的这个任务当中。(相当于一个应用程序的acitivity在另外一个应用程序显示出来,前提是另外一个应用程序与这个应用程序具有相同的affinity,allowTaskReparenting属性设置 成true时)

  • alwaysRetainTaskState:如果将最底层的那个Activity的这个属性设置为true,任务中所有的Activity即使过了很长一段时间之后仍然会被继续保留。

  • clearTaskOnLaunch:如果将最底层的那个Activity的这个属性设置为true,那么只要用户离开了当前任务,再次返回的时候就会将最底层Activity之上的所有其它Activity全部清除掉。简单来讲,就是一种和alwaysRetainTaskState完全相反的工作模式,它保证每次返回任务的时候都会是一种初始化状态,即使用户仅仅离开了很短的一段时间。

  • finishOnTaskLaunch:这个属性和clearTaskOnLaunch是比较类似的,不过它不是作用于整个任务上的,而是作用于单个Activity上。 如果某个Activity将这个属性设置成true,那么用户一旦离开了当前任务,再次返回时这个Activity就会被清除掉。 如果这个特性和allowTaskReparenting都设定为“true”,Activity的affinity忽略。这个Activity不会重新宿主,但是会销毁。

以上所有的介绍都是单一的,有时候相互结合可以根据各种特性结合起来思考就能达到很多意想不到的效果。