Android-Tasks and Back Stack
2014-11-03 15:15
309 查看
转载请标明出处:/article/1908011.html
原文:http://developer.android.com/guide/components/tasks-and-back-stack.html
一个应用一般都会包含多个activity。每一个activity都是围绕着用户的特定功能来实现的,并且是可以被别的activity启动起来的。比如:邮件应用可能有一个activity用来展示新邮件列表。当用户选择了一个邮件以后,会启动一个新的activity来展示邮件内容。
activity甚至可以启动设备上的别的应用。比如:你的应用想发送邮件,那你就可以定义一个做发送操作的intent,这里面会包含一些数据比如收件人的地址还有消息。另一个应用的activity如果它声明了能够处理这种类型的intent就可以启动来响应。这种情况下,intent是用来发送邮件的,所以邮件应用程序就启动起来了。如果多个activity都支持同样的intent,系统会让用户选择使用哪一个应用。当邮件发出去以后,你的activity会resume,看起来邮件的activity就像是你自己的应用的一部分一样。虽然activity是来自不同的应用的,android系统通过把这些activity放在同一个task中无缝的保持了用户体验。
task是用户为了完成特定任务而操作的activity的集合。这些activity以他们打开的顺序放在一个堆栈(back stack)中。
home键是大多数task开始的地方。当用户点击应用程序launcher的icon的时候(或者是home屏的快捷方式),应用程序的task会被放到前台。如果应用不存在于任何的task中(应用最近一直都没有使用过),系统会创建一个新的task,然后打开应用的主activity,并把它作为task的back stack的根activity。
当前activity打开一个新activity的时候,新activity会被放到back stack的栈顶,并且获取焦点。前一个activity仍然还在stack中,但是是处于stop状态。当一个activity进入stop的时候,系统会保持它的用户界面的状态。当用户按下back按键的时候,当前的activity会从stack的栈顶弹出(activity被销毁),之前的activity会进入resume(它的UI的状态会重建)。
stack中的activity的顺序永远都不会改变,只有可能在栈顶做push和pop,当被当前的activity启动的时候做push,当用户按下back键离开的时候做pop。back stack是后进先出的数据结构。表1可视化的展示了这种行为,按时间先后展示了activity和当前back stack的状态。
![](http://img.my.csdn.net/uploads/201411/03/1414998359_5426.png)
图1. 展示了task中的一个activity是如何向back stack中添加新的元素。当用户按下back键的时候,当前的activity会被销毁,前一个activity会被唤醒。
如果用户继续按下back键,task中的activity会依次弹栈,并展示前一个activity,一直到用户返回到home为止(或者是到task开始的activity为止)。当所有的activity都被从stack中弹出以后,task就也不再存在了。
task是一个紧密结合的整体,当用户启动一个新的task或者是按下home键退回到home屏的时候,task可以整体被移到后台。当task在后台的时候,task中的所有的activity都是处于stop状态的,但是task的back stack仍然是完整的。task只是被别的task夺去了焦点而已,如图2所示。
![](http://img.my.csdn.net/uploads/201411/03/1414998360_5008.png)
图2.两个task:Task B在前台接收用户的交互,Task A在后台等待唤醒。
task是可以返回到前台的,这样用户就可以重新回到他们当初离开的地方。假如当前的task(Task A)在它的back stack中有3个activity,当前的activity下面还有两个activity。这时候,用户按下了home键,然后启动一个新的应用。当home屏出现的时候,Task A就进入了后台。当心应用启动起来的时候,系统会给这个应用启动一个新的task(Task B),而且它也是有自己的back stack的。当跟这个应用做了一些交互以后,用户再次返回到了home,然后选中了启动Task A的应用。现在Task
A变为前台了,back stack中的3个activity都是完整的,并且栈顶的activity会被唤醒。这时候,用户也可以再切换到Task B,只要返回到home然后选中启动Task B的那个应用的icon就可以(或者是在overview screen中选中应用所在的task)这就是android上的一个多task的例子。
注意:同时可以在后台有多个task。但是,如果用户同时在后台运行多个task,为了回收内存,系统可能会销毁掉一些后台的activity,这回导致某些activity的状态丢失。下面的章节会介绍Activity的状态。
因为back stack中的Activity的顺序是永远都不会改变的,如果你的应用允许用户从多个Activity中启动某一个特定的Activity,这个Activity的一个新的实例会被创建出来,然后被压到栈顶(而不是把stack中应经存在是实例移动到栈顶)这样,你应用中的一个Activity可能会被实例化多次,就像图3那样。如果用户用back键回退,Activity的实例(带有他们自己的UI状态)会按照压栈的顺序依次被弹栈。但是,如果你不想让你的Activity被实例化多次的话,你也可以修改这种行为。稍后的章节会介绍如何管理Task。
Activity和task的默认行为总结如下:
(1)
当Activity A启动起来Activity B以后,Activity A就会stop,但是系统会保存它的状态(比如滚动的位置,表单输入的文本)。如果用户在Activity B中按下back键,Activity A会被唤醒,然后重建状态。
(2)
当用户按下home键离开一个task之后,当前的Activity会被stop,然后被置后台。系统会保存task中所有的Activity的状态。如果用户选中了启动task的那个应用的icon,task又会被放到前台,然后唤醒task中栈顶的Activity。
(3)
如果用户按下了back键,当前的Activity会被从stack中弹出然后被销毁,前一个Activity会被唤醒。当Activity被销毁以后,系统不再保存Activity的状态。
(4)
Activity可以被实例化多次,甚至是在不同的task中。
导航设计
关于更多android上导航的工作原理,可以参考android设计的导航指南(http://developer.android.com/design/patterns/navigation.html)
如何保存Activity的状态
正如之前讨论的那样,当Activity被stop以后,系统默认会保存它的状态。当用户back到之前的Activity的时候,它的UI还是它离开时的样子。但是,你可以而且是应该使用系统回调来保存你的Activity的状态,以防你的Activity被销毁了必须要重新创建。
当系统stop掉你的一个Activity以后(比如启动了一个新的Activity或者整个task被放到了后台),如果需要回收内存的话,系统可能会把这个Activity销毁掉。当发生这种情况的时候,Activity的状态就会丢失掉,但是系统仍然知道在回退栈中有这么一个Activity。但是,当这个Activity被放到栈顶的时候,系统必须要重新创建这个Activity(而不是唤醒)。为了避免用户的工作,你应该通过实现onSaveInstanceState()这个回调来保存Activity的状态。
关于如何保存Activity的状态,参考Activity的文档(http://developer.android.com/guide/components/activities.html#SavingActivityState)。
管理Task
上面介绍的android管理task和back stack的方式(把连续启动的Activity按照后入先出的形式放到同一个task中)对大多数应用来说都可以很好地工作,因为你不需要担心你的Activity是如何跟task关联上的,也不用关心Activity在back stack中是如何存在的。但是你可能会想要改变这种行为。你可能想让你应用中的Activity在启动起来的时候开始一个新的task(而不是放到当前的task中),或者当启动Activity的时候,你想使用已经存在的一个实例(而不是在栈顶创建一个新实例),或者当用户离开task的时候,你想清空你的back
stack,只留下根Activity。
通过设置manifest的<activity>元素的属性,或者是通过设置传递到startActivity()的intent的flag,就可以做这样的事情甚至更多。
本文中,相关的<activity>的属性:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
相关的flag:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
接下来的章节,你会看到如何使用这些属性和flag来定义Activity如何跟task关联还有Activity在back stack的行为。
关于task和Activity是如何在overview screen中展现和管理的将会被单独进行讨论,详细信息请参考:Overview Screen(http://developer.android.com/guide/components/recents.html)一般来说,你应该允许系统来定义你的task和Activity是如何在overview screen进行展示的,并且不需要来改变这种行为。注意:大多数应用都不应该改变Activity和task的默认行为。如果你决定让你的Activity改变默认的行为,必须要当心,要测试当启动或者按下back键从别的Activity导航到本Activity和task的时候确保Activity的可用性。尤其要测试那种可能跟用户预想的行为有冲突的那种导航行为。
定义启动模式launch mode
Launch mode允许你来定义一个新的Activity实例如何跟当前的task进行关联。
可以用下面两种方式来定义:
(1)使用manifest文件
当你在manifest文件中声明Activity的时候,可以指定当Activity启动的时候,如何跟task进行关联。
(2)使用Intent flags
当调用startActivity()的时候,可以在intent中包含一个flag,它就描述了新的Activity如何(是否)跟当前的task进行关联。
如果Activity A启动了Activity B,Activity B可以在manifest文件中定义它是如何跟当前的task进行关联的,Activity A也可以请求Activity B如何跟当前的task进行关联。如果这两个Activity都定义了Activity B跟当前task的关联方式,Activity A的请求(用intent来定义)会覆盖Activity B的请求(用manifest定义)。
注意:有些launch modes只对manifest文件可用,有些只对intent可用。
使用manifest文件
当在manifest文件中声明Activity的时候,可以使用<activity>元素的launchMode属性来制定Activity如何跟task相关联。launchMode属性指定了Activity如何被启动到一个task中,可以给launchMode属性设置4个不同的值。
"standard"
这是默认值。系统会在启动这个Activity的task中创建一个Activity的新实例,然后把intent传给它。Activity可以被实例化多次,不同的实例可以属于不同的task,一个task可以有多个实例。
"singleTop"
如果Activity的一个实例已经存在于当前task的栈顶,系统会调用这个实例的onNewIntent()并把intent传递给这个Activity,而不是创建一个Activity的新的实例。Activity可能会被创建多次,每一个实例都可以属于不同的task,一个task也可以有多个不同的实例(只要栈顶的Activity不是新创建的这个Activity就可以)
比如,假如说有一个task,它的back stack中有4个Activity,Activity A是跟Activity,然后一次是Activity B,C,D,Activity D是栈顶(栈里面是A-B-C-D)。这时候来了一个启动Activity D的intent,如果D是默认的launch mode,那么一个新的实例会被创建出来并压栈,栈就变成A-B-C-D-D。但是,如果D的launch mode是"singleTop",之前已经存在的那个D的实例会在onNewIntent()中接收到这个intent,因为D现在是在栈顶,然后栈里面还是A-B-C-D。但是,如果这时候来的是启动Activity
B的intent,一个新的Activity B的实例会被添加到stack中,虽然现在的launch mode是"singleTop"。
注意:当新的Activity实例被创建了以后,用户可以按下back键返回到之前的Activity。但是,当已经存在的Activity实例收到一个新的intent以后,在新的intent到达onNewIntent()之前,用户不能够按下back键返回到Activity之前的状态。
"singleTask"
系统会创建一个新的task,并且实例化一个Activity作为这个task的根Activity。但是,如果Activity有实例已经存在于另一个task中,系统会把intent发送到这个已经存在的实例上,然后调用它的onNewIntent()方法,而不是创建一个新的实例。同时只能存在Activity的一个实例。
注意:虽然Activity是以new task的方式启动的,但是按下back键仍然你可以回到之前的Activity。
"singleInstance"
与 ”singleTask”一样,但是系统不能启动别个 Activity到这个 Activity的实例所在的 task中去。这个 Activity总是单一的,且是他所在的 task的唯一成员。通过这个 Activity启动的其它任何 Activity都将单独启动一个单独的 task。
看另一个例子,android浏览器应用通过给<activity>元素指定singleTask模式声明了web浏览器的Activity总是在它自己的task中,这意味着,如果你的应用发起一个打开android浏览器的intent,它的Activity跟你应用是不再同一个task中的。相反,要么是给浏览器创建一个新的task,要么浏览器已经有一个在后台运行的task,这个后台task会被放到前台,然后处理这个新的intent。
不管新的Activity是不是跟启动它的Activity是在同一个task中,按下back键总能让用户返回到前一个Activity。但是,如果你使用singleTask模式启动了一个Activity,如果Activity有一个在后台task中的实例,整个后台task就会被放到前台。这时候,back stack会包含这个task中的所有的Activity,并且是在栈顶。图4说明了这种情况。
![](http://img.my.csdn.net/uploads/201411/03/1414998360_8650.png)
图4. 展示了"singleTask"启动起来的Activity是如何被加入到back stack的。如果Activity已经是后台task的一部分,那么整个的后台task的back stack也会变成前台,而且是在当前stack的栈顶。
想了解更多关于manifest文件中launch mode的信息,参考<activity>的文档,那里有跟多关于launchMode属性和可接受的值的讨论。
注意:用launchMode属性指定的Activity的行为是可以被用带有flag的intent启动的Activity所覆盖的。
使用Intent flag
当启动一个Activity的时候,可以通过给发送到startActivity()的intent添加flag来修改activity到task的默认关联方式。可以使用的修改默认行为的flag有:
FLAG_ACTIVITY_NEW_TASK
在一个新的task中启动Activity。如果你要启动的Activity已经存在于一个已经运行的task中,那么这个task就会被放到前台,并会重建task最后的状态,Activity会在onNewIntent()中收到新的intent。这和之前讨论的launchMode为"singleTask"的值的时候的行为是一样的。
FLAG_ACTIVITY_SINGLE_TOP
如果要启动的Activity就是当前的Activity(在back stack的栈顶),那么这个已经存在的实例会收到onNewIntent()调用,而不是创建一个新的Activity。这和之前讨论的launchMode为"singleTop"的值的时候的行为是一样的。
FLAG_ACTIVITY_CLEAR_TOP
如果要启动的Activity已经在当前的task中,那么位于这个Activity实例之上的所有的Activity都会被销毁掉,不是创建一个新的Activity实例,然后这个Activity会被唤醒,然后在onNewIntent()中收到intent。没有与之对应的launchMode的值。
FLAG_ACTIVITY_CLEAR_TOP经常跟FLAG_ACTIVITY_NEW_TASK一起使用。当一起使用的时候,这些flag可以用来定位到在其他的task中已经存在的Activity并把Activity放到一个能响应intent的位置上。
注意:如果Activity的launch mode是"standard",它也会被从stack中移除,然后一个新的Activity实例放到它的位置,并处理传进来的intent。这是因为当launch mode是"standard"的时候,总会给intent创建一个新的实例。
Handling affinity
affinity表明了Activity更喜欢属于哪一个task。默认情况下,同一个应用里面的Activity的affinity都是一样的。因此默认情况下,同一个应用里面的所有的Activity都喜欢在同一个task中。但是,你也可以修改Activity的默认的affinity。不同应用里面的Activity也可以共享一个affinity,同一个应用里面的Activity也可以有不同的affinity。
你可以修改<activity>元素的taskAffinity属性来修改指定activity的affinity。
taskAffinity属性接受string值,在<manifest>元素声明的默认包名中必须是唯一的,因为系统会使用这个名字来标识应用的默认task affinity。
affinity在两种情况下会有作用:
(1)当启动Activity的intent中包含了FLAG_ACTIVITY_NEW_TASK的时候。
默认情况下,一个新的Activity是通过调用startActivity()而被放到task中的。它会被放到跟调用者同一个task中。但是,如果intent中包含了FLAG_ACTIVITY_NEW_TASK,系统会给这个新的Activity查找一个不同的task。一般来说,会是一个新的task。但是,这不是必须的。如果已经存在一个跟这个新Activity有相同affinity的task的话,这个Activity会被放到这个task中。如果没有,就会被放到一个新的task中。
如果这个flag把Activity放到了一个新的task中,然后用户按下了home键离开了以后,必须要有一种方式能让用户返回到原来的task。有一些entity(比如提醒)总是在外部的task中打开Activity,而不是在他们自己的task中,所以,他们总是会在intent中给startActivity()传递FLAG_ACTIVITY_NEW_TASK。如果你有一个可以被外部的entity使用这个flag来调用的Activity,要注意用户有一种独立的返回到启动它的那个task的方式,比如:按下启动的icon(task的根Activity的intent
filter包含了CATEGORY_LAUNCHER,参考下面的开始一个task章节)。
(2)当Activity把它的allowTaskReparenting设置为true的时候。在这种情况下,Activity可以从启动它的task转移到有相同affinity的task,当task转到前台的时候。
比如说,一个报告选中城市天气状况的Activity被定义成旅游应用的一部分,天气Activity跟同应用中别的Activity有相同的affinity,使用这个属性就允许re-parenting。当某个Activity启动了天气Activity的时候,一开始的时候天气Activity跟启动他的Activity是在同一个task的。但是,当旅游应用的task转到前台的时候,天气Activity会被重新放置到旅游的task中,并在其中进行显示。
注意:从用户的角度来看,如果一个apk包含了多个“应用”的话,你可能就会使用taskAffinity给跟多个“应用”都有关联的Activity赋予不同的taskAffinity。
清空back stack
如果用户长时间离开一个task,系统会清理掉task中的所有的Activity,只保留根Activity。当用户返回到这个task的时候,只有根Activity会被重建。系统之所以这么做是因为当一段时间以后,用户很有可能已经抛弃掉他们之前的动作,返回到这个task是想做一些新东西。
Activity有一些属性可以用来修改这种行为:
alwaysRetainTaskState
如果task的根Activity的这个属性被设置为true的话,上面描述的那种行为就不复存在了。就算是长时间置后台,task仍然会在back stack中保留所有的Activity。
clearTaskOnLaunch
如果task的根Activity的这个属性被设置为true的话,只要用户离开task或者是返回到task的时候,stack都会被清理到只剩下根Activity。换句话说,这跟alwaysRetainTaskState恰好相反。就算是用户只离开了task一小会,当用户返回的时候task将会是它的初始的状态。
finishOnTaskLaunch
这个跟clearTaskOnLaunch很相似,但是它是作用与单个的Activity而不是整个的task。它也会导致Activity销毁,包括根Activity。当这个属性被设置为true的时候,Activity只有在当前回话里属于task的一部分,一旦用户离开,返回的时候Activity将不复存在。
开始一个task
要想把Activity设置为task的起点,只要给它的intent filter里面添加"android.intent.action.MAIN"的action并且"android.intent.category.LAUNCHER"的category就可以了。
比如:
这种类型的intent filter会让这个Activity以icon和label的形式展示在应用的launcher里面,这让用户可以启动这个Activity,然后可以在Activity启动以后的任何时候,都可以返回到它创建的task。
第二点非常重要,用户必须能够可以离开一个task然后可以使用Activity的launcher再回到它。基于这个原因,"singleTask"和"singleInstance"这两个把Activity标记为总是启动一个新的task的launch mode,只能当Activity的intent filter中包含ACTION_MAIN and a CATEGORY_LAUNCHER的时候才可以使用。假如没有这两个filter会怎样呢?intent启动了一个"singleTask"的Activity,初始化了一个新的task,用户在这个task上工作了一段时间。然后用户按下了home键,这个task会被放置到后台,然后变得不可见了。现在,用户就没有办法能够再回到这个task了,因为在应用的launcher里面找不到这个Activity。
在那些不需要让用户返回到Activity的场景,可以把<activity>的finishOnTaskLaunch属性设置为true。
跟多关于Activity和task是如何在overview screen中进行展现和管理的内容请参考Overview Screen(http://developer.android.com/guide/components/recents.html)。
做了一个小实验:
1.standard
FirstActivity->SecondActivity->FirstActivity->SecondActivity
FirstActivity<-SecondActivity<-FirstActivity
输出:
11-03 13:29:53.625: E/test(6173): FirstActivity,task id = 140,hashcode=1093231424
11-03 13:29:58.476: E/test(6173): SecondActivity,task id = 140,hashcode=1093341792
11-03 13:30:00.906: E/test(6173): FirstActivity,task id = 140,hashcode=1093412984
11-03 13:30:02.094: E/test(6173): SecondActivity,task id = 140,hashcode=1093481920
(1)同一个task
(2)每次都会创建新实例
(3)返回的时候,依次返回。
FirstActivity->SecondActivity->SecondActivity
FirstActivity<-SecondActivity
输出:
11-03 13:31:30.390: E/test(6275): FirstActivity,task id = 142,hashcode=1093205808
11-03 13:31:32.383: E/test(6275): SecondActivity,task id = 142,hashcode=1093315752
11-03 13:31:33.422: E/test(6275): SecondActivity,task id = 142,hashcode=1093387000
(1)同一个task
(2)从SecondActivity进入SecondActivity,也会创建新实例
(3)返回的时候,是从SecondActivity返回到SecondActivity,再返回到FirstActivity。
2.singleTop
FirstActivity->SecondActivity->FirstActivity->SecondActivity
FirstActivity<-SecondActivity<-FirstActivity<-
输出:
11-03 13:33:41.836: E/test(6803): FirstActivity,task id = 145,hashcode=1093231608
11-03 13:33:45.969: E/test(6803): SecondActivity,task id = 145,hashcode=1093341744
11-03 13:33:47.039: E/test(6803): FirstActivity,task id = 145,hashcode=1093412936
11-03 13:33:48.101: E/test(6803): SecondActivity,task id = 145,hashcode=1093481872
与standard相同。
FirstActivity->SecondActivity->SecondActivity
FirstActivity<-
输出:
11-03 13:34:56.617: E/test(6856): FirstActivity,task id = 147,hashcode=1093198584
11-03 13:35:00.336: E/test(6856): SecondActivity,task id = 147,hashcode=1093308720
11-03 13:35:01.406: E/test(6856): SecondActivity,onNewIntent, task id = 147,hashcode=1093308720
(1)同一个task
(2)从SecondActivity进入SecondActivity,并不会创建新实例,而是调用了onNewIntent。
(3)返回的时候,从SecondActivity直接返回到FirstActivity。
3.singleTask
MainActivity->FirstActivity->SecondActivity->FirstActivity->SecondActivity
208 ->209(1093202608)->209(1093345824)->209(1093202608)->209(1093447072)
MainActivity<-FirstActivity
输出:
(1)MainActivity打开FirstActivity是不同的task
(2)FirstActivity打开SecondActivity,同一个task。然后再打开FirstActivity,因为已经存在,所以直接调用FirstActivity的onNewIntent,
此时FirstActivity在栈顶,也就是说,已经把SecondActivity移除了。再打开SecondActivity,会重建一个SecondActivity。
(3)返回的时候,是从SecondActivity->FirstActivity->MainActivity。
4.singleInstance
<activity android:name="com.example.launchmodedemo.SecondActivity"
android:launchMode="standard"/>
MainActivity->FirstActivity->SecondActivity->FirstActivity->
215 ->216(1093202608)->217(1093345664)->216(1093202608)->217(1093345664)
MainActivity<-FirstActivity
(1)MainActivity打开FirstActivity是不同的task
(2)FirstActivity打开SecondActivity,也是不同的task,因为singleInstance自己位于一个task中。再打开FirstActivity会把FirstActivity置前台。
再打开SecondActivity(这里很奇怪,并没有新创建SecondActivity,而是直接使用的原来的SecondActivity)。
(3)返回的时候,从SecondActivity->FirstActivity->MainActivity。
注意:
(1)MainActivity是在另一个应用中,代码如下:
(2)SecondActivity的launchMode是standard,修改FirstActivity的launchMode进行测试。因为这两个参数只能作用于能在launch列表出现的那些Activity。
测试代码在这里:http://download.csdn.net/detail/goldenfish1919/8115145
(3)关于singletask补充一下,如果是在同一个app中,A是standard,B是singletask,C是standard,A启动B,B不会放在单独的task中,而是跟A在同一个task中。如果A->B->C->B,C到B的时候,会调用B的onNewIntent,并且C会被移出栈,此时back会直接到A。如果有另一个app2,app2的main启动了app的A,此时main和A是在同一个task中,此时的A再启动B,B会在另一个task中。
原文:http://developer.android.com/guide/components/tasks-and-back-stack.html
一个应用一般都会包含多个activity。每一个activity都是围绕着用户的特定功能来实现的,并且是可以被别的activity启动起来的。比如:邮件应用可能有一个activity用来展示新邮件列表。当用户选择了一个邮件以后,会启动一个新的activity来展示邮件内容。
activity甚至可以启动设备上的别的应用。比如:你的应用想发送邮件,那你就可以定义一个做发送操作的intent,这里面会包含一些数据比如收件人的地址还有消息。另一个应用的activity如果它声明了能够处理这种类型的intent就可以启动来响应。这种情况下,intent是用来发送邮件的,所以邮件应用程序就启动起来了。如果多个activity都支持同样的intent,系统会让用户选择使用哪一个应用。当邮件发出去以后,你的activity会resume,看起来邮件的activity就像是你自己的应用的一部分一样。虽然activity是来自不同的应用的,android系统通过把这些activity放在同一个task中无缝的保持了用户体验。
task是用户为了完成特定任务而操作的activity的集合。这些activity以他们打开的顺序放在一个堆栈(back stack)中。
home键是大多数task开始的地方。当用户点击应用程序launcher的icon的时候(或者是home屏的快捷方式),应用程序的task会被放到前台。如果应用不存在于任何的task中(应用最近一直都没有使用过),系统会创建一个新的task,然后打开应用的主activity,并把它作为task的back stack的根activity。
当前activity打开一个新activity的时候,新activity会被放到back stack的栈顶,并且获取焦点。前一个activity仍然还在stack中,但是是处于stop状态。当一个activity进入stop的时候,系统会保持它的用户界面的状态。当用户按下back按键的时候,当前的activity会从stack的栈顶弹出(activity被销毁),之前的activity会进入resume(它的UI的状态会重建)。
stack中的activity的顺序永远都不会改变,只有可能在栈顶做push和pop,当被当前的activity启动的时候做push,当用户按下back键离开的时候做pop。back stack是后进先出的数据结构。表1可视化的展示了这种行为,按时间先后展示了activity和当前back stack的状态。
![](http://img.my.csdn.net/uploads/201411/03/1414998359_5426.png)
图1. 展示了task中的一个activity是如何向back stack中添加新的元素。当用户按下back键的时候,当前的activity会被销毁,前一个activity会被唤醒。
如果用户继续按下back键,task中的activity会依次弹栈,并展示前一个activity,一直到用户返回到home为止(或者是到task开始的activity为止)。当所有的activity都被从stack中弹出以后,task就也不再存在了。
task是一个紧密结合的整体,当用户启动一个新的task或者是按下home键退回到home屏的时候,task可以整体被移到后台。当task在后台的时候,task中的所有的activity都是处于stop状态的,但是task的back stack仍然是完整的。task只是被别的task夺去了焦点而已,如图2所示。
![](http://img.my.csdn.net/uploads/201411/03/1414998360_5008.png)
图2.两个task:Task B在前台接收用户的交互,Task A在后台等待唤醒。
task是可以返回到前台的,这样用户就可以重新回到他们当初离开的地方。假如当前的task(Task A)在它的back stack中有3个activity,当前的activity下面还有两个activity。这时候,用户按下了home键,然后启动一个新的应用。当home屏出现的时候,Task A就进入了后台。当心应用启动起来的时候,系统会给这个应用启动一个新的task(Task B),而且它也是有自己的back stack的。当跟这个应用做了一些交互以后,用户再次返回到了home,然后选中了启动Task A的应用。现在Task
A变为前台了,back stack中的3个activity都是完整的,并且栈顶的activity会被唤醒。这时候,用户也可以再切换到Task B,只要返回到home然后选中启动Task B的那个应用的icon就可以(或者是在overview screen中选中应用所在的task)这就是android上的一个多task的例子。
注意:同时可以在后台有多个task。但是,如果用户同时在后台运行多个task,为了回收内存,系统可能会销毁掉一些后台的activity,这回导致某些activity的状态丢失。下面的章节会介绍Activity的状态。
因为back stack中的Activity的顺序是永远都不会改变的,如果你的应用允许用户从多个Activity中启动某一个特定的Activity,这个Activity的一个新的实例会被创建出来,然后被压到栈顶(而不是把stack中应经存在是实例移动到栈顶)这样,你应用中的一个Activity可能会被实例化多次,就像图3那样。如果用户用back键回退,Activity的实例(带有他们自己的UI状态)会按照压栈的顺序依次被弹栈。但是,如果你不想让你的Activity被实例化多次的话,你也可以修改这种行为。稍后的章节会介绍如何管理Task。
Activity和task的默认行为总结如下:
(1)
当Activity A启动起来Activity B以后,Activity A就会stop,但是系统会保存它的状态(比如滚动的位置,表单输入的文本)。如果用户在Activity B中按下back键,Activity A会被唤醒,然后重建状态。
(2)
当用户按下home键离开一个task之后,当前的Activity会被stop,然后被置后台。系统会保存task中所有的Activity的状态。如果用户选中了启动task的那个应用的icon,task又会被放到前台,然后唤醒task中栈顶的Activity。
(3)
如果用户按下了back键,当前的Activity会被从stack中弹出然后被销毁,前一个Activity会被唤醒。当Activity被销毁以后,系统不再保存Activity的状态。
(4)
Activity可以被实例化多次,甚至是在不同的task中。
导航设计
关于更多android上导航的工作原理,可以参考android设计的导航指南(http://developer.android.com/design/patterns/navigation.html)
如何保存Activity的状态
正如之前讨论的那样,当Activity被stop以后,系统默认会保存它的状态。当用户back到之前的Activity的时候,它的UI还是它离开时的样子。但是,你可以而且是应该使用系统回调来保存你的Activity的状态,以防你的Activity被销毁了必须要重新创建。
当系统stop掉你的一个Activity以后(比如启动了一个新的Activity或者整个task被放到了后台),如果需要回收内存的话,系统可能会把这个Activity销毁掉。当发生这种情况的时候,Activity的状态就会丢失掉,但是系统仍然知道在回退栈中有这么一个Activity。但是,当这个Activity被放到栈顶的时候,系统必须要重新创建这个Activity(而不是唤醒)。为了避免用户的工作,你应该通过实现onSaveInstanceState()这个回调来保存Activity的状态。
关于如何保存Activity的状态,参考Activity的文档(http://developer.android.com/guide/components/activities.html#SavingActivityState)。
管理Task
上面介绍的android管理task和back stack的方式(把连续启动的Activity按照后入先出的形式放到同一个task中)对大多数应用来说都可以很好地工作,因为你不需要担心你的Activity是如何跟task关联上的,也不用关心Activity在back stack中是如何存在的。但是你可能会想要改变这种行为。你可能想让你应用中的Activity在启动起来的时候开始一个新的task(而不是放到当前的task中),或者当启动Activity的时候,你想使用已经存在的一个实例(而不是在栈顶创建一个新实例),或者当用户离开task的时候,你想清空你的back
stack,只留下根Activity。
通过设置manifest的<activity>元素的属性,或者是通过设置传递到startActivity()的intent的flag,就可以做这样的事情甚至更多。
本文中,相关的<activity>的属性:
taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainTaskState
finishOnTaskLaunch
相关的flag:
FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP
接下来的章节,你会看到如何使用这些属性和flag来定义Activity如何跟task关联还有Activity在back stack的行为。
关于task和Activity是如何在overview screen中展现和管理的将会被单独进行讨论,详细信息请参考:Overview Screen(http://developer.android.com/guide/components/recents.html)一般来说,你应该允许系统来定义你的task和Activity是如何在overview screen进行展示的,并且不需要来改变这种行为。注意:大多数应用都不应该改变Activity和task的默认行为。如果你决定让你的Activity改变默认的行为,必须要当心,要测试当启动或者按下back键从别的Activity导航到本Activity和task的时候确保Activity的可用性。尤其要测试那种可能跟用户预想的行为有冲突的那种导航行为。
定义启动模式launch mode
Launch mode允许你来定义一个新的Activity实例如何跟当前的task进行关联。
可以用下面两种方式来定义:
(1)使用manifest文件
当你在manifest文件中声明Activity的时候,可以指定当Activity启动的时候,如何跟task进行关联。
(2)使用Intent flags
当调用startActivity()的时候,可以在intent中包含一个flag,它就描述了新的Activity如何(是否)跟当前的task进行关联。
如果Activity A启动了Activity B,Activity B可以在manifest文件中定义它是如何跟当前的task进行关联的,Activity A也可以请求Activity B如何跟当前的task进行关联。如果这两个Activity都定义了Activity B跟当前task的关联方式,Activity A的请求(用intent来定义)会覆盖Activity B的请求(用manifest定义)。
注意:有些launch modes只对manifest文件可用,有些只对intent可用。
使用manifest文件
当在manifest文件中声明Activity的时候,可以使用<activity>元素的launchMode属性来制定Activity如何跟task相关联。launchMode属性指定了Activity如何被启动到一个task中,可以给launchMode属性设置4个不同的值。
"standard"
这是默认值。系统会在启动这个Activity的task中创建一个Activity的新实例,然后把intent传给它。Activity可以被实例化多次,不同的实例可以属于不同的task,一个task可以有多个实例。
"singleTop"
如果Activity的一个实例已经存在于当前task的栈顶,系统会调用这个实例的onNewIntent()并把intent传递给这个Activity,而不是创建一个Activity的新的实例。Activity可能会被创建多次,每一个实例都可以属于不同的task,一个task也可以有多个不同的实例(只要栈顶的Activity不是新创建的这个Activity就可以)
比如,假如说有一个task,它的back stack中有4个Activity,Activity A是跟Activity,然后一次是Activity B,C,D,Activity D是栈顶(栈里面是A-B-C-D)。这时候来了一个启动Activity D的intent,如果D是默认的launch mode,那么一个新的实例会被创建出来并压栈,栈就变成A-B-C-D-D。但是,如果D的launch mode是"singleTop",之前已经存在的那个D的实例会在onNewIntent()中接收到这个intent,因为D现在是在栈顶,然后栈里面还是A-B-C-D。但是,如果这时候来的是启动Activity
B的intent,一个新的Activity B的实例会被添加到stack中,虽然现在的launch mode是"singleTop"。
注意:当新的Activity实例被创建了以后,用户可以按下back键返回到之前的Activity。但是,当已经存在的Activity实例收到一个新的intent以后,在新的intent到达onNewIntent()之前,用户不能够按下back键返回到Activity之前的状态。
"singleTask"
系统会创建一个新的task,并且实例化一个Activity作为这个task的根Activity。但是,如果Activity有实例已经存在于另一个task中,系统会把intent发送到这个已经存在的实例上,然后调用它的onNewIntent()方法,而不是创建一个新的实例。同时只能存在Activity的一个实例。
注意:虽然Activity是以new task的方式启动的,但是按下back键仍然你可以回到之前的Activity。
"singleInstance"
与 ”singleTask”一样,但是系统不能启动别个 Activity到这个 Activity的实例所在的 task中去。这个 Activity总是单一的,且是他所在的 task的唯一成员。通过这个 Activity启动的其它任何 Activity都将单独启动一个单独的 task。
看另一个例子,android浏览器应用通过给<activity>元素指定singleTask模式声明了web浏览器的Activity总是在它自己的task中,这意味着,如果你的应用发起一个打开android浏览器的intent,它的Activity跟你应用是不再同一个task中的。相反,要么是给浏览器创建一个新的task,要么浏览器已经有一个在后台运行的task,这个后台task会被放到前台,然后处理这个新的intent。
不管新的Activity是不是跟启动它的Activity是在同一个task中,按下back键总能让用户返回到前一个Activity。但是,如果你使用singleTask模式启动了一个Activity,如果Activity有一个在后台task中的实例,整个后台task就会被放到前台。这时候,back stack会包含这个task中的所有的Activity,并且是在栈顶。图4说明了这种情况。
![](http://img.my.csdn.net/uploads/201411/03/1414998360_8650.png)
图4. 展示了"singleTask"启动起来的Activity是如何被加入到back stack的。如果Activity已经是后台task的一部分,那么整个的后台task的back stack也会变成前台,而且是在当前stack的栈顶。
想了解更多关于manifest文件中launch mode的信息,参考<activity>的文档,那里有跟多关于launchMode属性和可接受的值的讨论。
注意:用launchMode属性指定的Activity的行为是可以被用带有flag的intent启动的Activity所覆盖的。
使用Intent flag
当启动一个Activity的时候,可以通过给发送到startActivity()的intent添加flag来修改activity到task的默认关联方式。可以使用的修改默认行为的flag有:
FLAG_ACTIVITY_NEW_TASK
在一个新的task中启动Activity。如果你要启动的Activity已经存在于一个已经运行的task中,那么这个task就会被放到前台,并会重建task最后的状态,Activity会在onNewIntent()中收到新的intent。这和之前讨论的launchMode为"singleTask"的值的时候的行为是一样的。
FLAG_ACTIVITY_SINGLE_TOP
如果要启动的Activity就是当前的Activity(在back stack的栈顶),那么这个已经存在的实例会收到onNewIntent()调用,而不是创建一个新的Activity。这和之前讨论的launchMode为"singleTop"的值的时候的行为是一样的。
FLAG_ACTIVITY_CLEAR_TOP
如果要启动的Activity已经在当前的task中,那么位于这个Activity实例之上的所有的Activity都会被销毁掉,不是创建一个新的Activity实例,然后这个Activity会被唤醒,然后在onNewIntent()中收到intent。没有与之对应的launchMode的值。
FLAG_ACTIVITY_CLEAR_TOP经常跟FLAG_ACTIVITY_NEW_TASK一起使用。当一起使用的时候,这些flag可以用来定位到在其他的task中已经存在的Activity并把Activity放到一个能响应intent的位置上。
注意:如果Activity的launch mode是"standard",它也会被从stack中移除,然后一个新的Activity实例放到它的位置,并处理传进来的intent。这是因为当launch mode是"standard"的时候,总会给intent创建一个新的实例。
Handling affinity
affinity表明了Activity更喜欢属于哪一个task。默认情况下,同一个应用里面的Activity的affinity都是一样的。因此默认情况下,同一个应用里面的所有的Activity都喜欢在同一个task中。但是,你也可以修改Activity的默认的affinity。不同应用里面的Activity也可以共享一个affinity,同一个应用里面的Activity也可以有不同的affinity。
你可以修改<activity>元素的taskAffinity属性来修改指定activity的affinity。
taskAffinity属性接受string值,在<manifest>元素声明的默认包名中必须是唯一的,因为系统会使用这个名字来标识应用的默认task affinity。
affinity在两种情况下会有作用:
(1)当启动Activity的intent中包含了FLAG_ACTIVITY_NEW_TASK的时候。
默认情况下,一个新的Activity是通过调用startActivity()而被放到task中的。它会被放到跟调用者同一个task中。但是,如果intent中包含了FLAG_ACTIVITY_NEW_TASK,系统会给这个新的Activity查找一个不同的task。一般来说,会是一个新的task。但是,这不是必须的。如果已经存在一个跟这个新Activity有相同affinity的task的话,这个Activity会被放到这个task中。如果没有,就会被放到一个新的task中。
如果这个flag把Activity放到了一个新的task中,然后用户按下了home键离开了以后,必须要有一种方式能让用户返回到原来的task。有一些entity(比如提醒)总是在外部的task中打开Activity,而不是在他们自己的task中,所以,他们总是会在intent中给startActivity()传递FLAG_ACTIVITY_NEW_TASK。如果你有一个可以被外部的entity使用这个flag来调用的Activity,要注意用户有一种独立的返回到启动它的那个task的方式,比如:按下启动的icon(task的根Activity的intent
filter包含了CATEGORY_LAUNCHER,参考下面的开始一个task章节)。
(2)当Activity把它的allowTaskReparenting设置为true的时候。在这种情况下,Activity可以从启动它的task转移到有相同affinity的task,当task转到前台的时候。
比如说,一个报告选中城市天气状况的Activity被定义成旅游应用的一部分,天气Activity跟同应用中别的Activity有相同的affinity,使用这个属性就允许re-parenting。当某个Activity启动了天气Activity的时候,一开始的时候天气Activity跟启动他的Activity是在同一个task的。但是,当旅游应用的task转到前台的时候,天气Activity会被重新放置到旅游的task中,并在其中进行显示。
注意:从用户的角度来看,如果一个apk包含了多个“应用”的话,你可能就会使用taskAffinity给跟多个“应用”都有关联的Activity赋予不同的taskAffinity。
清空back stack
如果用户长时间离开一个task,系统会清理掉task中的所有的Activity,只保留根Activity。当用户返回到这个task的时候,只有根Activity会被重建。系统之所以这么做是因为当一段时间以后,用户很有可能已经抛弃掉他们之前的动作,返回到这个task是想做一些新东西。
Activity有一些属性可以用来修改这种行为:
alwaysRetainTaskState
如果task的根Activity的这个属性被设置为true的话,上面描述的那种行为就不复存在了。就算是长时间置后台,task仍然会在back stack中保留所有的Activity。
clearTaskOnLaunch
如果task的根Activity的这个属性被设置为true的话,只要用户离开task或者是返回到task的时候,stack都会被清理到只剩下根Activity。换句话说,这跟alwaysRetainTaskState恰好相反。就算是用户只离开了task一小会,当用户返回的时候task将会是它的初始的状态。
finishOnTaskLaunch
这个跟clearTaskOnLaunch很相似,但是它是作用与单个的Activity而不是整个的task。它也会导致Activity销毁,包括根Activity。当这个属性被设置为true的时候,Activity只有在当前回话里属于task的一部分,一旦用户离开,返回的时候Activity将不复存在。
开始一个task
要想把Activity设置为task的起点,只要给它的intent filter里面添加"android.intent.action.MAIN"的action并且"android.intent.category.LAUNCHER"的category就可以了。
比如:
<activity ... > <intent-filter ... > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> ... </activity>
这种类型的intent filter会让这个Activity以icon和label的形式展示在应用的launcher里面,这让用户可以启动这个Activity,然后可以在Activity启动以后的任何时候,都可以返回到它创建的task。
第二点非常重要,用户必须能够可以离开一个task然后可以使用Activity的launcher再回到它。基于这个原因,"singleTask"和"singleInstance"这两个把Activity标记为总是启动一个新的task的launch mode,只能当Activity的intent filter中包含ACTION_MAIN and a CATEGORY_LAUNCHER的时候才可以使用。假如没有这两个filter会怎样呢?intent启动了一个"singleTask"的Activity,初始化了一个新的task,用户在这个task上工作了一段时间。然后用户按下了home键,这个task会被放置到后台,然后变得不可见了。现在,用户就没有办法能够再回到这个task了,因为在应用的launcher里面找不到这个Activity。
在那些不需要让用户返回到Activity的场景,可以把<activity>的finishOnTaskLaunch属性设置为true。
跟多关于Activity和task是如何在overview screen中进行展现和管理的内容请参考Overview Screen(http://developer.android.com/guide/components/recents.html)。
做了一个小实验:
1.standard
<activity android:name="com.example.launchmodedemo.FirstActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.example.launchmodedemo.SecondActivity" android:launchMode="standard"/>
FirstActivity->SecondActivity->FirstActivity->SecondActivity
FirstActivity<-SecondActivity<-FirstActivity
输出:
11-03 13:29:53.625: E/test(6173): FirstActivity,task id = 140,hashcode=1093231424
11-03 13:29:58.476: E/test(6173): SecondActivity,task id = 140,hashcode=1093341792
11-03 13:30:00.906: E/test(6173): FirstActivity,task id = 140,hashcode=1093412984
11-03 13:30:02.094: E/test(6173): SecondActivity,task id = 140,hashcode=1093481920
(1)同一个task
(2)每次都会创建新实例
(3)返回的时候,依次返回。
FirstActivity->SecondActivity->SecondActivity
FirstActivity<-SecondActivity
输出:
11-03 13:31:30.390: E/test(6275): FirstActivity,task id = 142,hashcode=1093205808
11-03 13:31:32.383: E/test(6275): SecondActivity,task id = 142,hashcode=1093315752
11-03 13:31:33.422: E/test(6275): SecondActivity,task id = 142,hashcode=1093387000
(1)同一个task
(2)从SecondActivity进入SecondActivity,也会创建新实例
(3)返回的时候,是从SecondActivity返回到SecondActivity,再返回到FirstActivity。
2.singleTop
<activity android:name="com.example.launchmodedemo.FirstActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.example.launchmodedemo.SecondActivity" android:launchMode="singleTop"/>
FirstActivity->SecondActivity->FirstActivity->SecondActivity
FirstActivity<-SecondActivity<-FirstActivity<-
输出:
11-03 13:33:41.836: E/test(6803): FirstActivity,task id = 145,hashcode=1093231608
11-03 13:33:45.969: E/test(6803): SecondActivity,task id = 145,hashcode=1093341744
11-03 13:33:47.039: E/test(6803): FirstActivity,task id = 145,hashcode=1093412936
11-03 13:33:48.101: E/test(6803): SecondActivity,task id = 145,hashcode=1093481872
与standard相同。
FirstActivity->SecondActivity->SecondActivity
FirstActivity<-
输出:
11-03 13:34:56.617: E/test(6856): FirstActivity,task id = 147,hashcode=1093198584
11-03 13:35:00.336: E/test(6856): SecondActivity,task id = 147,hashcode=1093308720
11-03 13:35:01.406: E/test(6856): SecondActivity,onNewIntent, task id = 147,hashcode=1093308720
(1)同一个task
(2)从SecondActivity进入SecondActivity,并不会创建新实例,而是调用了onNewIntent。
(3)返回的时候,从SecondActivity直接返回到FirstActivity。
3.singleTask
<activity android:name="com.example.launchmodedemo.FirstActivity" android:label="@string/app_name" android:launchMode="singleTask" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name="com.example.launchmodedemo.SecondActivity" android:launchMode="standard"/>
MainActivity->FirstActivity->SecondActivity->FirstActivity->SecondActivity
208 ->209(1093202608)->209(1093345824)->209(1093202608)->209(1093447072)
MainActivity<-FirstActivity
输出:
(1)MainActivity打开FirstActivity是不同的task
(2)FirstActivity打开SecondActivity,同一个task。然后再打开FirstActivity,因为已经存在,所以直接调用FirstActivity的onNewIntent,
此时FirstActivity在栈顶,也就是说,已经把SecondActivity移除了。再打开SecondActivity,会重建一个SecondActivity。
(3)返回的时候,是从SecondActivity->FirstActivity->MainActivity。
4.singleInstance
<activity android:name="com.example.launchmodedemo.FirstActivity" android:label="@string/app_name" android:launchMode="singleInstance" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
<activity android:name="com.example.launchmodedemo.SecondActivity"
android:launchMode="standard"/>
MainActivity->FirstActivity->SecondActivity->FirstActivity->
215 ->216(1093202608)->217(1093345664)->216(1093202608)->217(1093345664)
MainActivity<-FirstActivity
(1)MainActivity打开FirstActivity是不同的task
(2)FirstActivity打开SecondActivity,也是不同的task,因为singleInstance自己位于一个task中。再打开FirstActivity会把FirstActivity置前台。
再打开SecondActivity(这里很奇怪,并没有新创建SecondActivity,而是直接使用的原来的SecondActivity)。
(3)返回的时候,从SecondActivity->FirstActivity->MainActivity。
注意:
(1)MainActivity是在另一个应用中,代码如下:
Button btn1 = (Button) this.findViewById(R.id.btn1); btn1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String packageName1 = "com.example.launchmode"; String className = "com.example.launchmodedemo.FirstActivity"; Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); ComponentName cn = new ComponentName(packageName1, className); intent.setComponent(cn); startActivity(intent); MainActivity.this.startActivity(intent); } });
(2)SecondActivity的launchMode是standard,修改FirstActivity的launchMode进行测试。因为这两个参数只能作用于能在launch列表出现的那些Activity。
测试代码在这里:http://download.csdn.net/detail/goldenfish1919/8115145
(3)关于singletask补充一下,如果是在同一个app中,A是standard,B是singletask,C是standard,A启动B,B不会放在单独的task中,而是跟A在同一个task中。如果A->B->C->B,C到B的时候,会调用B的onNewIntent,并且C会被移出栈,此时back会直接到A。如果有另一个app2,app2的main启动了app的A,此时main和A是在同一个task中,此时的A再启动B,B会在另一个task中。
相关文章推荐
- Android官方开发指南-Tasks and Back Stack
- 【Android】任务和返回栈(tasks and back stack)
- Android 阅读Tasks and Back Stack文章后的重点摘抄
- Android Api Component---翻译任务和回退栈(Tasks and Back Stack)
- Android Notes 05 - Tasks and Back Stack
- android 之 Tasks and Back Stack
- android的Tasks and Back Stack
- android之Activity的Tasks and Back Stack
- Android Tasks and Back Stack (一)
- Android-Tasks and Back Stack
- android Tasks and Back Stack
- Android官方文档之App Components(Tasks and Back Stack)
- Android Tasks and Back Stack
- 【Android】任务和返回栈(tasks and back stack)
- 【Android】任务和返回栈(tasks and back stack)
- Android Dev Guide---Tasks and Back Stack(上)
- Android任务与返回堆栈 (Tasks and Back Stack)
- Android - 《Tasks and Back Stack》
- Android任务和返回栈(Tasks and Back Stack)
- Android Dev Guide---Tasks and Back Stack(下)