原文地址:http://developer.android.com/guide/components/tasks-and-back-stack.html

一个应用往往包含很多activities.每个activity都应围绕着用户可执行的特定动作来设计,并且可以启动其它activities.例如,一个email应用可能有一个显示新邮件列表的activity.当用户选择一个邮件,一个新的activity被打开以显示邮件内容.

  一个activity也可以打开同一设备上存在于其它应用的activities。例如,如果你的应用想要发送一个邮件,你可以定义一个intent来执行一个"send"动作并包含一些数据,比如一个地址和一条信息.另一个应用中的activity可以自己处理这种intent的然后被打开(如果有多个activitie支持同样的intent,那么系统会让用户选择一个).当email被发送后,你的activity被恢复并且看起来发送邮件的activity好像是你的应用的一部分.即使那些activities可能来自不同的应用,Android也靠着把两个activity保存在同一个任务中来实现这种无缝的用户体验.

  一个任务是用户在执行某种工作时所需要的activities的集合.activities们放置在一个栈("back stack")中,按照打开的顺序排列.

  设备的Home屏是大多数任务的开始场所.当用户触摸在应用启动台中的图标(或一个home屏上的快捷方式)时,应用的任务就来到了前台.如果没有这个应用已存在的任务(这个应用最近没有被使用),那么一个新的任务被创建并且这个应用的"main"activity被作为栈的根activity打开.

  当前的activity启动了另一个activity,新的activity被放置在栈顶并拥有焦点.先前的activity依然保存在栈中,但是停止了.当一个activity停止时,系统保存了它的用户界面的当前状态.当用户点击后退按钮时,当前的activity被从栈顶弹出(activity被销毁了)并且先前的activity被恢复了.栈中的activities永不会被重新排列,只会进行入栈或出栈行为—当被当前activity启动时就入栈,当用户使用后退按钮离开它时就出栈.如此,后退栈也是一个后进先出的栈.

下图按照时间轴展示了activities在stack中的行为变化。


        图1,展示了一个task中的一个新的activity作为一个item加入到back stack中。当用户按了返回按钮时,当前的activity将被销毁,前一个activity将复活。

  如果用户继续后退,那么栈中的每个activity被弹出来然后展示上一个,直到用户退到Home屏(或到达任务开始时运行的那个activity).当所有的activities都从棧种移除,任务就不再存在.

  一个任务是一个有聚合力的单元,它可以在用户启动一个新的任务或回到home屏时被整体地移到后台.当位于后台时,任务中的所有的activities都处于停止,但是任务的后退栈却保存完整—当任务被另一个任务取代时,仅仅是失去了焦点.见图2:

图2. 两个任务:任务B到了前台,任务A于是被打入后台,伺机恢复.

  一个任务可以再回到前台,于是用户可以获得他离开时的模样.举个例子,当前的任务(任务A)有三个activities在其栈中—两个在下面.用户按下Home 按钮,然后又启动一个新的应用.当Home屏出现时,任务A到了后台.当新应用启动时,系统为这个应用开始了一个任务(任务B).当使用完新应用时,用户再次回到了Home屏然后选择了启动任务A的那个应用.现在,任务A来到了前台栈中所有的三个activities都完整保留并且位于顶层的activity被恢复.此时,用户也可以再回到home屏然后选择任务B的应用于是回到任务B(或通过长按Home 按钮以显示最近的任务然后选择它).

注:多个任务可以同时存在于后台.然而,如果用户在同一时刻运行多个后台任务,系统可能会销毁后台activities来釋放内存,从而导致activity状态的丢失.

  因为后退栈中的activities从不会被重排,如果你的应用允许用户从不只一个activity启动一个特殊的activity,一个新的activity的实例会被创建并压入栈中(而不是把这个activity的当前实例弄到前台来).所以,你的应用中的一个activity可能被多次实例化(甚至是从不同的任务),如图3所示.同样的,如果用户使用后退按钮向后导航,activity的每个实例都会按照打开的顺序重新显现(每个都保持它们自己的状态).然后,你如果不想某个activity被实例化多次,你可以改变这种行为.后面会讲到如何做.

 

3.一个activity被实例化多次.

下面总结一下下activity和任务的默认行为:

  • 当ActivityA启动ActivityB,ActivityA停止,但是系统保存它的状态(比如滚动条的位置和表单中输入的文本).如果用户在Activity B中按下了后退按钮,ActivityA以保存的状态恢复.

  • 当用户按下Home按钮离开了一个任务,当前的activity停止同时它的任务进入后台.系统保持任务中每个activity的状态.如果用户后来运行了这个任务的应用而恢复了这个任务,任务回到前台并使栈顶端的activity恢复.

  • 如果用户按下了后退按钮,当前的activity从栈中弹出并被销毁.前一个activity被恢复.当一个activity被销毁时,系统不再保持activity的状态.

  • Activitie可以被多次实例化,即使是从另外的任务.

保存Activity的状态

  如上所述,系统默认下会在activity停止的时候保存其状态.如此一来,当用户导航到前一个activity时,其用户介面显示得跟离开时一样.然后,你应该使用activity的回调方法们保持它的状态,因为activity可能会被销毁然后被重新创建.当系统停止了你的一个activities(比如当新的activity启动或任务被移到后台),系统可能为了释放内存会完全销毁那个activity.当这事发生时,activity的状态丢失.如果真发生了这种现象,系统依然知道那个activity在后退栈中占有一个位置,但是当activity被弄到前台时,系统必须重新创建它(而不是仅仅恢复它).为了避免丢掉用户的工作,你应该通过实现activity的onSaveInstanceState()来提前保存状态.

  关于保存activitystate的更多知识,请看 Activities 

管理tasks

  Android管理任务和后退栈的方式,如前面文章所述—通过把所有接连启动的activity放在同一个任务中并且是同一个后进先出的栈中—在大多数应用中工作得很好并且你无需关心你的activity如何与任务相关连或如何在后退栈中存在.然而,你可能决定要打破这种正常的行为.可能你想在你应用的activity启动时开始一个新的任务(而不是放置到当前栈中);或者,当你启动一个activity,你想把已经运行的它的一个实例提到前台来(而不是创建一个新的实例放在后退栈的顶端);或者,你希望当用户离开任务时,你的后退栈清除了根activity以外的所有activity.

  可以做这些事情,甚至更多事情,通过设置manifest中<activity>的属性和传到startActivity()的intent的flag.

在这一点上,你可以设置的最重要的<activity>属性有:

taskAffinity

launchMode

allowTaskReparenting

clearTaskOnLaunch

alwaysRetainTaskState

finishOnTaskLaunch

可以使用的最重要的intent flag:

FLAG_ACTIVITY_NEW_TASK

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_SINGLE_TOP

注意:大多数应用不应改变activity和任务的默认行为.如果你确定必须要改变默认行为,你必需小心并且保证测试了activity在启动时和后退导航到它时的可用性.确保测试了与用户习惯相冲突的导航行为.

定义启动模式

  启动模式使你可以定义新的activity如何与当前的任务相关联.有两种方法来定义不同的启动模式:

  • 使用manifest文件

      当你在你的manifest文件中声明一个activity时,你可以指定activity在启动时如何与任务相关联.

  • 使用Intent的flag

      当你调用startActivity()时,你可以在Intent中包含指明新的activity如何(或是否)与当前栈关联的flag.

  同样的,如果ActivityA启动ActivityB,ActivityB可以在它的manifest中定义如何与当前的任务关联(如果真的存在)并且ActivityA也可以请求让ActivityB如何与当前的任务关联.如果两个activity都定义了ActivityB如何与任务关联,那么ActivityA的请求(在intent中定义)优先于ActivityB的请求(在它的manifest中定义).

注:一些启动模式可以用在manifest中但不能用在intentflag上,同样的,一些启动模式可以用在intentflag上但不能用在manifest中.

使用manifest文件

  当在你的manifest文件中声明一个activity时,你可以使用<activity>元素的launchMode属性指定activity如何与一个任务关联.

  launchMode属性指明了activity如何启动到一个任务中去.有四种不同的启动模式你可以用于指定给launchMode属性:

  • "standard"(默认模式)

      默认.系统创建一个新的activity的实例到启动它的任务中.activity可以被多次实例化,每个实例可以属于不同的任务,也可以属于同一个任务.

  • "singleTop"

      如果一个activity的实例已经存在于当前任务的栈的顶端,系统通过调用它的onNewIntent()方法把intent路由到这个实例,而不是创建一个新的实例.activity可以被多次实例化,每个实例可以属于不同的任务,并且一个任务可以具有多个实例(但只是当位于后退栈的顶端的activity不存在时才会出现这种现像).

      例如,假设一个任务的后退栈中有根ActivityA和activityB,C,D(A-B-C-D;D位于顶端).一个intent到达了D类型的activity(不是指这里的acitivityD).如果D具有默认的"standard"启动模式,一个新的类的实例被启动并且栈变为A-B-C-D-D.然而,如果D的启动模式是"singleTop",那么这个已存在的ActivityD就通过onNewIntent()接收到intent,因为它在栈的顶端—栈于是依然保持A-B-C-D.又然而,如果一个intent到达了B类型的activity(不是此处的activityB),那么一个新的B实例被添加到栈中,即使它的启动模式是"singleTop".

      注:当一个新的activity的实例被创建,用户可以按下后退键回到上一个activity.但当一个已存在的activity实例处理了一个新intent,用户就不能按下后退键回到当前的activity在intent来之前的状态.

  • "singleTask"

      系统创建一个新的任务并且实例化activity为新任务的根.然而,如果一个activity的实例已存在于另一个任务,系统就会通过调用这个activity的onNewIntent()把intent路由给它,而不是创建一个新的实例.某个时刻只有一个activity的实例可以存在.

      注:尽管activity在一个新任务中启动,后退键依然可以返回到上一个activity.

  • "singleInstance".

      跟"singleTask"一样.除了系统不能再启动其它activity到拥有这个activity实例的任务中.activity永远是任务的唯一;任何由这个activity启动的其它activity都在另一个任务中打开.

举一个例子,Android浏览器应用声明网页浏览activity必须在它自己的任务中打开—通过在<activity>元素中指定singleTask启动模式.这表示如果你的应用发出一个intent来打开Android浏览器,它的activity不会放到你的应用所在的任务中.代替的是,可能一个新的任务为浏览器启动,或者,如果浏览器已经运行于后台,它所在的任务就被弄到前台并接受这个intent.

  不论一个从一个新任务启动activity还是在一个已存在这种activity的任务中启动,后退键总是能后退到前一个activity.然而,如果你在任务A中启动一个声明为singleTask模式的activity,而这个activity可能在后台已有一个属于一个任务(任务B)的实例.任务B于是被弄到前台并处理这个新的intent.那么此时后退键会先一层层返回任务BActivity,然后再返回到任务A的顶端activity.图4演示了这种情形.

4.演示一个"singleTask"启动模式的acitvity如何被添加到一个后退栈中.如果这个activity已经是一个后台任务(任务B)自己的栈的一部分,那么整个后退栈被弄到前台,位于当前任务(任务A)的上面.

使用 Intentflags

  当启动一个activity时,你可以在给startActivity()的intent中包含flag以改变activity与任务的默认关联方式.你可以用来改变默认行为的flag有:

  • FLAG_ACTIVITY_NEW_TASK

      在新的任务中启动activity-即不在本任务中启动.如果一个包含这个activity的任务已经在运行,那个任务就被弄到前台并恢复其UI状态,然后这个已存在的activity在onNewIntent()中接收新的intent.

      这个标志产生与"singleTask"相同的行为.

  • FLAG_ACTIVITY_SINGLE_TOP

      如果正启动的activity就是当前的activity(位于后退栈的顶端),那么这个已存在的实例就接收到onNewIntent()的调用,而不是创建一个新的实例.

      这产生与"singleTop"模式相同的行为.

  • FLAG_ACTIVITY_CLEAR_TOP

      如果要启动的activity已经在当前任务中运行,那么在它之上的所有其它的activity都被销毁掉,然后这个activity被恢复,而且通过onNewIntent(),initent被发送到这个activity(现在位于顶部了)

      没有launchMode属性值对应这种行为.

      FLAG_ACTIVITY_CLEAR_TOP多数时候与FLAG_ACTIVITY_NEW_TASK联用.当一起使用时,会在其它任务中寻找一个已存在的activity实例并其把它放到一个可以响应intent的位置.

    注:如果Activity的启动模式是"standard",FLAG_ACTIVITY_CLEAR_TOP会导致已存在的activity被从栈中移除然后在这个位置创建一个新的实例来处理到来的intent.这是因为"standard"模式会导致总是为新的intent创建新的实例.

处理任务亲和力

  亲和力表明了一个activity"心仪"哪个任务.默认下,属于同一个应用的所有activities之间具有相同的任务亲和力.所以,默认下,一个应用的所有activities首选属于同一任务.然而,你可以修改一个activity的默认任务亲和力.定义于不同应用的Activities可以具有相同的任务亲和力,或者同一应用中的activities可以分配不同的任务亲和力.

  你可以使用<activity>元素的taskAffinity属性来修改一个activity的任务亲和力.taskAffinity属性使用字符串作为值,这个字符串必须与在<manifest>中声明的默认包名不同,因为系统使用包名来标识默认的任务亲和力.

  亲和力在以下两种情况起作用:

  • 当启动一个activity的intent包含FLAG_ACTIVITY_NEW_TASK标志.

      一个新的activity默认是在调用startActivity()的activity所在的任务中安置.然而,如果传给startActivity()的intent包含了FLAG_ACTIVITY_NEW_TASK标志,系统就会查找另一个能安置这个新activity的任务.通常,它会是一个新任务.然而但是,并不是必须这样做.如果有一个已存在的任务具有与新activity相同的亲和力,那么这个activity就被启动并安置到这个已存在的任务中.如果没有这样的任务,才开始一个新的任务.

      如果这个标志导致了一个activity在一个新的任务中启动然后用户按下了HOME键离开了这个新任务,那么必须有一些方法使得用户可以重新回到这个任务.一些实体(比如通知管理器)总是在一个另外的任务中启动activity而从不作为自己任务的一部分,于是它总是把FLAG_ACTIVITY_NEW_TASK设置到传给startActivity()的intents中.如果你有一个activity可以被外部实体使用这个标志调用,应小心用户可能用一个独立的方法回到这个启动的任务,比如使用启动图标(任务的根activity有一个CATEGORY_LAUNCHERintent过滤器).-翻译得挺难受,这句话也就是说,只要使用了相同的亲和力,用户就能回到这个已启动的任务中.

  • 当一个activity的allowTaskReparenting属性为"true"时.

      在此情况下,activity可以从启动它的任务移动到一个亲和的任务中,当后一个任务来到前台时.

      例如,假设一个报告所选城市的天气状况的activity是作为一个旅游应用的一部分.它与同一个应用中的其它activity具有相同的亲和力(默认的application亲合力)并且它被允许重认父母.当你的一个activity启动了这个天气预报activity,它起初是与你的actvity属于同一个任务.然而,当旅游应用的任务进入前台时,天气预报activity就被重新分配到这个任务并在其只显示.

小提示::如果一个.apk文件包含多个从用户角度所认为的"应用",你可能想通过为activity指定属性taskAffinity来使它们连接到不同的"应用"

清空后退栈

  如果用户离开了一个任务很长一段时间,系统会清空任务中除了根activity之外的所有其它activity.当用户重新返回这个任务时,只有根activity被恢复.系统之所以这样做,是因为经过一大段时间之后,用户很可能已抛弃掉他们已经做的并且回到任务开始做一些新的事情.

  有一些activity属性你可以用来改变这种行为:

  • alwaysRetainTaskState

      如果任务的根activity的这个属性被设置为"true",前面所述的默认行为就不会发生.任务保持所有的后退栈中的activity,即使经过很长一段时间.

  • ClearTaskOnLaunch

      如果任务的根activity的这个属性被设置为"true",在用户离开任务再回来时,栈中是清空到只剩下根activity.换句话说,它是与alwaysRetainTaskState反着来的.用户回到任务时永远见到的是初始状态,即使只离开了一小会.

  • finishOnTaskLaunch

      这个属性很像clearTaskOnLaunch,但是它作用于一个单独的activity,而不是整个任务.它也可以导致任何activity死亡,包含根activity.当它被置为"true"时,activity只在当前会话中存活.如果用户离开然后回来,它就已经不在了.

启动一个task

  你可以设置一个activity为一个任务的入口,通过给它一个值为"android.intent.action.MAIN"的intent过滤器"和一个值为"android.intent.category.LAUNCHER"的过滤器.例如:

<activity... >

<intent-filter... >

<actionandroid:name="android.intent.action.MAIN" />

<categoryandroid:name="android.intent.category.LAUNCHER" />

</intent-filter>

...

</activity>

  一个intent这种类型的过滤器导致activity的一个图标和标签被显示于应用启动界面上.使得用户可以启动这个activity并且再次回到这个任务.

  这第二个能力是很重要的:用户必须能离开一个任务并且之后还能通过启动器回来.为此,两种使得activity永远在新任务中启动的启动模式:"singleTask"和"singleInstance",应该只在当activity具有ACTION_MAIN和CATEGORY_LAUNCHER过滤器时使用.想像一下,例如,如果没有这些过滤器将会发生什么:一个intent启动一个"singleTask"activity,在一个新的任务中初始化,并且用户在这个任务中忙乎了一些时间.然后用户按下HOME按钮.任务现在被移到后台并且不可见了.因为这个activity不是应用的启动activity,用户就再也没有办法回到这个任务了.

  但遇到那些你不希望用户能够回到一个activity的情况时怎么办呢?有办法:设置<activity>元素的finishOnTaskLaunch属性为"true"!

转载请说明出处:http://blog.csdn.net/ff20081528/article/details/17219951

Tasks and Back stack 详解的更多相关文章

  1. Java容器解析系列(4) ArrayList Vector Stack 详解

    ArrayList 这里关于ArrayList本来都读了一遍源码,并且写了一些了,突然在原来的笔记里面发现了收藏的有相关博客,大致看了一下,这些就是我要写的(╹▽╹),而且估计我还写不到博主的水平,这 ...

  2. 2.5 C++STL stack详解

    文章目录 2.5.1引入 2.5.2 代码示例 2.5.3 代码运行结果 总结 2.5.1引入 stack是一种"先进后出"的容器. 不过值得注意的是stack是一种关联容器,是通 ...

  3. flutter系列之:flutter中常用的Stack layout详解

    [toc] 简介 对于现代APP的应用来说,为了更加美观,通常会需要用到不同图像的堆叠效果,比如在一个APP用户背景头像上面添加一个按钮,表示可以修改用户信息等. 要实现这样的效果,我们需要在一个Im ...

  4. java三篇博客转载 详解-vector,stack,queue,deque

    博客一:转载自http://shmilyaw-hotmail-com.iteye.com/blog/1825171 java stack的详细实现分析 简介 我们最常用的数据结构之一大概就是stack ...

  5. ASP.NET MVC深入浅出系列(持续更新) ORM系列之Entity FrameWork详解(持续更新) 第十六节:语法总结(3)(C#6.0和C#7.0新语法) 第三节:深度剖析各类数据结构(Array、List、Queue、Stack)及线程安全问题和yeild关键字 各种通讯连接方式 设计模式篇 第十二节: 总结Quartz.Net几种部署模式(IIS、Exe、服务部署【借

    ASP.NET MVC深入浅出系列(持续更新)   一. ASP.NET体系 从事.Net开发以来,最先接触的Web开发框架是Asp.Net WebForm,该框架高度封装,为了隐藏Http的无状态模 ...

  6. Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理

    本文非常详尽地介绍了Java中的三个集合类 ArrayList,Vector与Stack <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整 ...

  7. STL stack 常见用法详解

    <算法笔记>学习笔记 stack 常见用法详解 stack翻译为栈,是STL中实现的一个后进先出的容器.' 1.stack的定义 //要使用stack,应先添加头文件#include &l ...

  8. Elastic Stack 笔记(六)Elasticsearch5.6 搜索详解

    博客地址:http://www.moonxy.com 一.前言 Elasticsearch 主要包含索引过程和搜索过程. 索引过程:一条文档被索引到 Elasticsearch 之后,默认情况下 ES ...

  9. Linux进程上下文切换过程context_switch详解--Linux进程的管理与调度(二十一)

    1 前景回顾 1.1 Linux的调度器组成 2个调度器 可以用两种方法来激活调度 一种是直接的, 比如进程打算睡眠或出于其他原因放弃CPU 另一种是通过周期性的机制, 以固定的频率运行, 不时的检测 ...

随机推荐

  1. perl的Getopt::Long和pod::usage ?

    来源: http://www.cnblogs.com/itech/archive/2012/08/07/2627267.html 代码: 需要显式地定义变量且初始化.例如optionX. 如果没有定义 ...

  2. Shell script fails: Syntax error: “(” unexpected

    Shell script fails: Syntax error: “(” unexpected google 一下. http://unix.stackexchange.com/questions/ ...

  3. C#之控制台输入和输出

    控制台输出 C# 控制台程序一般使用 .NET Framework Console 类提供的输入/输出服务.Console.WriteLine("Hello World!"); 语 ...

  4. Python作用域

    以下依据Python 3 1.Python变量查找顺序为LEGB(L:Local,E:Enclosing,G:Global,B:Built-in). 2.实际上,在Python中,只有模块,类以及函数 ...

  5. bash和sh区别

    在一般的linux系统当中(如redhat),使用sh调用执行脚本相当于打开了bash的POSIX标准模式(等效于bash的 --posix 参数),一般的,sh是bash的“子集”,不是子集的部分. ...

  6. UIImagePikerController 浅析

    原文链接:http://www.jianshu.com/p/2ac85aca4468 UIImagePickerController是iOS系统提供的和系统的相册和相机交互的一个类,可以用来获取相册的 ...

  7. Jmeter的优点是什么?除了轻量级,它和LoadRunner有什么本质区别

    1.jmeter的架构和loadrunner原理一样,都是通过中间代理,监控和收集并发客户端发出的指令,把他们生成脚本,再发送到应用服务器,再监控服务器反馈结果的一个过程: 2.分布式中间代理功能在j ...

  8. cocos2d-x 3.3 显示中文

    Resources文件夹下的strings.xml: <dict> <key>targetScore</key> <string>目标分数</st ...

  9. Zencart批量删除无图片产品

    Zencart批量删除无图片产品 2012-04-23 07:26:18|  分类: 默认分类 |字号 订阅 转自 http://zhongjia33.blog.163.com/blog/#m=0   ...

  10. 解决phpmyadmin上传文件大小限制的配置方法(转)

    phpmyadmin导入SQL文件时涉及到phpmyadmin上传文件大小限制问题,默认phpmyadmin上传文件大小为2M,如果想要 phpmyadmin上传超过2M大文件,就需要修改phpmya ...