Android Api Component---翻译任务和回退栈(Tasks and Back Stack)

卧疾丰暇豫,翰墨时间作。这篇文章主要讲述Android Api Component---翻译任务和回退栈(Tasks and Back Stack)相关的知识,希望能为你提供帮助。
一个应用程序通常包括多个activity。每个activity应当环绕一个指定的用户能够运行的而且能够开启其他activity的动作种类被设计。
比如,一个emali应用程序或许有一个activity展示新消息列表。
当用户选择了一个消息的时候,一个新的activity会打开查看这个消息。


一个activity甚至能够开启设备上的其他应用程序的activity。
比如,假设你的应用程序向发送一个邮件消息,你能够定义一个intent运行一个"send"动作而且包括一些像email地址和消息的数据。来自于还有一个应用程序的activity定义他自己使用这样的intent打开。
在这这个样例中,这个intent将发送一个email,因此一个email应用程序的"compose"activity开启(假设多个activity支持同样的intent,那么系统会让用户选择使用哪一个)。
当email发送之后,你的activity恢复而且它就好像这个邮件activity是你的应用程序的一部分。
虽然应用程序之间的activity是不同的,可是android通过保持全部的activity在同样的task中无缝的维护这些用户体验。



一个任务是当运行某种工作时与用户交互的activity的集合。activity被安排在一个栈中(回退栈),每个activity会依照进入的顺序被打开。



设备的Home屏幕是大多数任务開始的地方。
当用户在一个应用程序启动器里按了一个图标时,应用程序的任务就被调到前台。假设给这个应用程序没有任务存在(应用程序近期已经被使用了)。那么一个新的任务会被创建而且"main"activity在那个栈中给那个应用程序以根activity打开。


当当前的activity打开还有一个的时候,新的activity被推到栈顶而且取得焦点。
前面的activity任然在战中,可是被停止了。当一个activity停止的时候,系统回收当前的用户接口的状态。当用户按了返回button。当前的activity会从栈顶被弹出(这个activity被销毁)而且前面的activity恢复(前面的UI的状态被恢复)。
在栈中的activity用户不会被又一次整理,仅仅有从栈中推送和弹出-当在当前activity中开启的时候推送到栈中而且当用户使用返回button离开的时候被弹出。像这样。回退栈操作作为一个“后进先出”的对象结构。下图用时间线展示了用当前的回退栈处理activity之间准时的每个点的行为。

Android Api Component---翻译任务和回退栈(Tasks and Back Stack)

文章图片

这个图代表了每一个新的activity在任务中怎样加入项到后台栈中。
当用户按了回退button的时候,当前的activity被销毁而且前一个activity又一次開始。


假设用户继续按返回,在栈中的每个activity将被弹出去展现前一个activity,直到用户返回到主屏幕未知(或者当任务開始的时候不管哪一个正在执行的activity)。当全部的activity从栈中被移除的时候,这个任务也就不再存在。



当用户開始一个新任务或者通过Home键进入主屏幕的时候,一个任务是一个能够移动到“后台”的连续单元。然后在后台。在任务中的全部activity都会被停止,可是对这个任务的回退栈仍然是完整的-指示当还有一个任务进行的时候。这个任务仅仅是失去了焦点,正例如以下图展示的一样。一个任务然后能够返回到“前端”以至于用户能够获得他们离开时的位置。增加,比如,当前的任务(Task A)在它的栈中有三个activity-两个在当前activity的以下。用户按了Home键,然后从这个应用程序启动器开启一个新的应用程序。当主屏幕出现的时候。Task A进入后台。当新应用程序開始的时候,系统给这个应用程序(Task B)用它拥有的activity的栈開始了一个任务。在跟那个应用程序交互之后,用户又返回Home而且选择了那个原始的被开启的Task A。如今,Task A来到了前端-在它的栈中的全部三个activity是完整的而且在栈顶的activity又一次開始。此时,用户能够通过进入Home转换返回到Task B而且选择那个被開始的任务的应用程序图标(或者通过从概览屏幕选择app的任务)。这是Android上的一个多任务的样例。


Android Api Component---翻译任务和回退栈(Tasks and Back Stack)

文章图片

注意:多任务能在后台被马上操作。然而。假设用户同一时候执行着非常多后台任务,系统或许会依照回收内存的顺序销毁后台activity,这样就导致那些activity的状态将丢失。
关于Activity state看以下的段落。



因为在回退栈中的activity永远不会被又一次整理。假设你的应用程序同意用户開始超过一个的一个特别的activity,那个activity的实例被创建而且被推送的栈中(而不是带入不论什么activity实例的上面)。像这种,在你的应用程序中的一个activity或许被初始化多次(甚至来自于不同的任务)。就像在下图被展示的一样。像这种情况。假设用户使用返回button导航返回,每个activity的实例依照他们被打开的顺序显示(每个用他们自己的UI状态)。
然而,假设你不想让一个activity被初始化多次,你能够改动这种行为。怎样这样做,将会在以下的Managing Tasks段落中被讨论。

Android Api Component---翻译任务和回退栈(Tasks and Back Stack)

文章图片

来给activity和task的行为做一个总结:


        当Activity A开启Activity B的时候,Activity A被停止。可是系统保留着它的状态(像滚动位置和输入表单的文本)。假设用户在Activity B中按了返回button,Activity A用它的状态又一次開始被恢复。



        当用户通过按Home键离开任务的时候,当前的activity被停止而且它的任务进入后台。系统保留着每一个在任务中的activity的状态。
假设用户后来通过选择启动器图标開始任务又一次開始了这个任务,那么这个任务会进入前端而且在栈顶又一次開始这个activity。


        假设用户按了返回button,当前的activity从栈中被弹出而且被销毁。在栈中的前面的activity被又一次開始。当一个activity被销毁的时候,系统不保留activity的状态。



    activity能够被初始化多次。甚至从其他任务中初始化。


导航设计
对于app在android上假设导航工作的,读Android Design‘s Navigation文档    


管理Task 
在上面被描写叙述的Android管理任务和回退栈的这样的方式。通过把全部的被開始的activity放在同样的任务中而且用“后进先出”栈-对于大多数的应用程序起非常多的作用而且你不须要操心你的activity怎样跟任务关联或者他们在回退栈中是怎样存在的。然而,你可能决定你想要终端正常的行为。
或许你想让你的应用程序中的activity被開始的时候(取代放在当前任务内)開始一个新的任务。或者当你開始一个activity的时候,你想让一个存在的activity的实例走上前(取代在栈顶创建一个新的实例);或者当用户离开任务的时候你想让你的回退栈清除全部的activity,除了根activity。



你但是使用在< 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


在以下的段落中你将看到你怎样使用那些清单属性和intent的flag来定义activity怎样跟task关联以及他们在回退栈中怎样执行。


还有,分开讨论activity和task是考虑到task和activity或许被描写叙述或者被管理在那个综述屏幕中。
看很多其它Overview Screen的信息。通常你应该同意系统定义你的task和activity在综合屏幕中被描写叙述,而且你不须要改动这样的行为。


注意:大多数应用程序不会中端给activity和task的默认的行为。假设你决定你的activity须要就该默认行为。使用警告而且确保在执行期測试以及从其他的activity和任务使用返回button导航回退到它的时候activity的可用性。
确保測试导航行为与用户的期望行为的冲突。


定义启动模式

启动模式同意你定义一个新的activity的实例假设跟当前任务关联起来。你能够用两种方式定义不同的启动模式:



使用清单列表
当你在你的manifest文件里定义了一个activity的时候。你当它開始的时候你能够指定这个activity怎样跟任务关联。

使用Intent的flag
当你调用startActivity()的时候。你应该在Intent中包括一个flag。这个flag定义了这个新的activity怎样(或者是否)应该和当前任务关联。



像这种。假设Activity A开启Activity B。Activity B能够在它的manifest中定义它应该怎样跟当前的task(或者全部)关联。而且Activity A也能够要求Activity B应该怎样跟当前任务关联。
假设两个activity都定义了Activity B怎样跟一个task关联,那么Activity A的需求(在intent中被定义的)遵守于Activity B的需求(在它的manifest中被定义)。



注意:一些有效的启动模式对于这个manifest文件的intent的flag是无效的,相同的。一些给intent的flag的有效的启动模式不能在manifest中被定义。




使用manifest文件
当在你的manifest文件里定义了一个activity的时候,你能够指定这个activity使用< activity> 元素的lanuchMode属性应该怎样跟一个任务关联。



这个lanuchMode属性指定了一个关于这个activity应当怎样进入一个任务被执行的指令。
有四个不同的启动模式你能够分配给launchMode属性:


"standard" (默认模式)
        默认的,系统在任务中创建一个activity的实例,这个activity从这个任务中被开启并且把intent路由给这个activity。这个activity能被初始化多次,每个实例可能属于不同的任务。并且一个任务可能有多个实例。



"singleTop"
        假设一个activity的实例已经存在于当前任务的顶部,那么系统通过调用给它的onNewIntent()把这个intent路由到那个实例,然不是创建一个activity的新实例。这个activity可能会被初始化多次,每个实例可能属于不同的任务,而且一个任务可能有多个实例(可是假设这个activity在回退栈的顶部,那就仅仅有一个activity的实例存在)。



        比如。加入一个任务的后台栈用activity B,C和D在顶端组成了根activity A。
一个给activity D的intent到达。
假设D有默认的"standard"启动模式,一个新的类实例就被启动而且这个栈会变成A-B-C-D-D。然而。假设D的启动模式是"singleTop",那么D重已经存在的实例通过onNewIntent()接收这个intent。由于他在栈的顶部-这个栈任然是A-B-C-D。可是。假设有个给activity B的intent到达。那么一个B的实例将被加入到这个栈中,甚至假设它的启动模式是"singleTop"。



注意:当一个activity实例被创建的时候,用户可能案子啊回退button返回到前一个activity。可是当一个activity的实例已经存在的时候运用一个新的intent。在新的intent到达onNewIntent()里面之前,用户不能按返回button来回到上一个activity的状态。



"singleTask"
        系统创建一个新任务而且在新任务的根部初始化这个activity。然而,假设这个activity的实例已经存在于一个分离的任务中,系统就通过调用给它的onNewIntent()方法路由intent到已经存在的activity的实例中,然不是创建一个新的实例。在同一时间,仅仅有一个activity的实例能够存在。


        注意:虽然activity開始在一个新的任务中,可是回退button仍然能够给用户返回到前一个activity。



"singleInstance"
        除了系统不启动不论什么其他的activity进入这个持有这个实例的任务中。其他的跟"singleTask"一样。这个activity总是单个的,而且它的任务仅仅有一个成员;任被开启的activity通过这一个切割的任务打开。


作为还有一个样例。Android浏览器应用程序通过在< activity> 元素中指定singleTask启动模式定义web浏览器应该总是在它自己的任务中打开。这就意味着,假设你的应用程序公布了一个打开Android Browser的intent,它的activity不会被放在跟你的应用程序同样的任务中。相反。要么開始一个新的任务来开启浏览器,假设浏览器已经后台任务中执行,那个任务会被带到前面来操作这个新的intent。


无论是否一个activity开启在新任务或者同样任务中,就好像是这个activity开启了它,回退button总是将用户带到前一个activity。
然而。假设你開始一个指定了singleTask启动模式的activity,那么假设一个activity的实例存在于一个后台任务中,整个任务会被带到前面。此时,后台栈在它的顶部如今包括了全部来自于被带到前面的这个任务的activity。
例如以下图:

Android Api Component---翻译任务和回退栈(Tasks and Back Stack)

文章图片



关于在manifest文件里很多其它使用启动模式的信息。看< activity> 元素文档,在哪里lanuchMode属性和被接收的值被讨论很多其它。


注意:你给你的activity用launchMode属性指定的行为能够被Intent所带的flags覆盖开启你的activity,接下来会讨论这个。



使用Intent的flag

当你開始一个activity的时候。你可能通过包括在你传递给startActivity()的intent中的flag改动了一个activity的默认的关联给它的任务。你能用这些flag改动默认行为的是:


FLAG_ACTIVITY_NEW_TASK
    在一个新的任务中开启activity。假设一个任务已经给你如今正在开启的activity执行着。那么那个任务会用它的最后的状态被恢复来带到前端而且这个activity在onNewIntent()中接收一个新的intent。


    这个产生的行为跟在前面讨论过的"singleTask"launchMode值是相似的。



FLAG_ACTIVITY_SINGLE_TOP
        假设被开启的activity是当前activity(在回退栈顶部),那么这个已经存在的实例取代创建一个新的activity实例接收一个调用到onNewIntent()。

        这个跟前面讨论的"singleTop"launchMode值产生同样的行为。




FLAG_ACTIVITY_CLEAR_TOP
        假设被启动的activity已经执行在当前任务中,那么取代执行一个activity的新实例,在这个activity的上面的全部其他activity被销毁而且这个通过onNewIntent()给这个被又一次開始的activity的实例传递了这个intent。




        没有launchMode属性值产生这样的行为。



        FLAG_ACTIVITY_CLEAR_TOP是最长跟FLAG_ACTIVITY_NEW_TASK结合使用。当一起呗使用的时候,那些flag事一种本地化一个已经在其他觉得中存在的activity的方式而且把它放在一个能响应intent的地方。



注意:假设这个activity的指定的启动模式是"standard",那它也会从这个栈中被移除而且在它的位置一个新的实例被启动来操作进入的intent。
那是由于当启动模式是"standard"的时候,一个新实例总是为了一个新的intent被创建。


处理关系(Handling affinities)

这个关系指明一个activity更喜欢属于哪一个task。默认的,来自于同样应用程序的全部activity彼此有一个关系。因此。默认的,在同样应用程序中的全部的activity更喜欢在同样的任务中。然而,你能够给activity改动默认关系。被定义在不同应用程序中的activity能够共享一个关系。或者被定义在同样应用程序中的activity能够被安排在不同的任务关系中。


你能够使用< activity> 元素中的taskAffinity属性来改动被给定的这个activity的关系。


这个taskAffinity属性取一个字符串值,在< manifest> 元素中定义的这个属性它必须在默认的包名中是唯一的,由于系统给应用程序使那个名字来分辨默认的任务关系。



在两种环境下这个关系会開始起作用:



        当包括这个FLAG_ACTIVITY_NEW_TASK标记的intent调用startActivity()启动一个activity的时候。你一个新的activity默认的被执行在activity的任务中。它作为调用者被推到同样的后台栈中。
然而。假设被传递给startActivity()的intent包括FLAG_ACTIVITY_NEW_TASK标记,系统寻找一个不同的任务来给新的activity空间。通常,它是个新的任务。然而,它不须要是。假设作为新的activity在已经存在的任务中有了同样关系的activity。这个activity被启动到这个任务中。
假设没有,它会開始一个新任务。

假设这个标记导致一个activity開始一个新的任务而且用户按Home键离开它,那就必需要有一些方式能够给用户导航回到那个任务。一些实体(像通知管理)总是在一个内部任务中开启activity,从来不会作为他们拥有的部分。因此他们总是把FLAG_ACTIVITY_NEW_TASK放入它们传递给startActivity()的intent中。
【Android Api Component---翻译任务和回退栈(Tasks and Back Stack)】假设你有一个activity能够通过内部实体调用或许就是用了这个标记,注意用户有一个独立的方式能够回到他们打开的那个任务,像用启动器图标(任务的根activity有一个CATEGOR_LAUNCHER的intent过滤器。看以下的Starging a task段落)。


        当一个activity把它的allowTaskReparenting属性设置为"true"。


        在这个样例中。当任务来到前端的时候。这个activity能够从这个开启它的有一个关系的任务中移除。


        比如。如果在被选择的城市的一个天气预报的activity状态被定义为旅游应用程序的部分。它跟其他的那些在同样应用程序中的activity有同样的关系(默认应用程序的关系)而且它同意父类使用这个属性。当你的当中的一个activity开启了这个天气预报的activity的时候,它最初跟你的activity属于同样的任务。可是。当旅游应用程序的任务回到前台的时候,天气预报activity被安排到那个任务中而且在它里面被陈列。



建议:假设一个.apk文件从你用户的角度来看包括超过一个"application"。你可能想要使用taskAffinity属性分配不同的关系给那些跟每个"application"关联的activity。



清除回退栈
假设用户离开一个任务非常长时间,系统会把任务中的除了根activity以外的全部activity清除掉。当用户又返回到任务的时候。仅仅有根activity被恢复。
系统表现的这样的方式。由于,非常长时间以后。用户非常可能有已经遗弃了它们之前正在做的和返回到任务開始新的事情。



你能够用activity的一些属性来改动这样的行为:


alwaysRetainTaskState
        假设这个属性在任务的根activity中被设置为"true",被描写叙述的默认行为就不会发生了。这个任务回收在它的任务中的全部activity甚至在一段时间之后。



clearTaskOnLaunch
        假设这个属性在一个任务的根activity中被设置为"true",不管用户什么时候离开而且返回,栈都会被清理到根activity。换句话说。它是alwaysRetainTaskState的对立面。
用户总是返回到任务的初始状态,甚至在离开了任务非常长时间之后。



finishOnTaskLaunch
        这个属性像clearTaskOnLaunch,可是它操作单个的activity。而不是整个任务。
它也能够引起不论什么activity离开,包含根activity。
当它被设置为"true",这个activity在当前session下任然是这个任务的一部分。
假设用户离开了而且又回到这个任务,它不再呈现。




開始一个任务
你能够设置一个activity作为一个任务的入口点,通过给它一个intent带有"android.intent.action.MAIN"的过滤器当做被指定的动作还有"android.intent.category.LAUNCHER"当做被指定的类别。比如:
< activity ...> < intent-filter ...> < action android:name="android.intent.action.MAIN"/> < category android:name="android.intent.category.LAUNCHER"/> < /intent-filter> < /activity>

一个这样的intent的过滤器目标是一个给activity的icon和标签将在应用程序启动器中被陈列。给用户一种启动这个activity的和返回到这个它被启动之后的不论什么时间创建任务的方式。


这个的第二个功能是重要的:用户必须能离开任务而且然后后来使用这个activity的启动器回来。对于这样的原因,这两个启动模式标记activity好像总是初始化一个任务,"singleTask"和"singleInstance"。仅仅有当activity有一个ACTION_MAIN和一个CATEGORY_LAUNCHER过滤器的时候才应该被使用。想象一下,比如,假设过滤器丢失了可能会发生什么:一个intent启动一个"singleTask"activity,初始化一个新任务,而且用户花费一些时间在那个任务中工作。
用户然后按了Home键。任务如今被发送到后台而且不可见了。如今用户没有方式回到这个任务,由于它在这个应用程序启动器中没有被描写叙述。



对于那些你不想让用户能回到一个activity的情况。设置< activity> 的元素的finishOnTaskLaunch为"true"(看 Clearing the stack)。

?
































































































    推荐阅读