本文部分参考自 http://blog.csdn.net/mayingcai1987/article/details/6200909 ,部分内容自己总结,错误之处请指正
1、android:launchMode=”standard” 标准启动模式。这种启动模式也是Activity默认的,一个栈中可以有多个相同的Activity。不会考虑是否栈中是否有相同的Activity。比如设置A为standard模式,那么可以运行A-B-A-A-A;
2、android:launchMode=”singleTop” 单一栈顶启动模式, 又叫栈顶复用模式,栈顶只能存在一个相同的Activity。比如你栈顶是MainActivity.java,你想启动新的MainACtivity.java是无法实现的。但是栈中可以存在多个MainActivity.java实例。 比如设置A为singleTop模式,运行 A-B-A 这里栈中可以同时存在了两个A,但是如果A已经在栈顶了就不能在A之上再启动一个A,这时候虽然系统不会调用A的onCreate(),但会调用onNewIntent(),即位于栈顶的activity被复用。
当一个Activity已经在栈顶,但依然有可能启动它,而你又不想产生新的Activity实例,此时就可以用singleTop模式。
3、android:launchMode=”singleTask” 单一栈启动模式,又叫栈内复用模式,当使用这种启动模式的时候,栈中只能存在一个相同Activity的实例。比如设置A为singleTask模式,那么执行 A-B-C-D ,此时D在栈顶,你想启动一个A,此时会直接启动在栈底层的A,而不会去新建一个A,并且此时A已经成为了栈顶了,所以C和D都是被移出栈,也就是被销毁了,同理系统不会调用A的onCreate(),但会调用onNewIntent(),即栈内的activity被复用。
singleTask模式和前面两种模式的最大区别就是singleTask模式是任务内单例的,所以我们是否设定Activity为singleTask模式,就是看我们activity是否需要单例,例如你的某个Activity里面有一个列表,如果有多个实例,有可能导致用户看到的列表不一致,有的Activity需要经常启动,如果每次都创建实例,会导致占用资源过多,这些情况都可以使用singleTask模式(例如Launcher应用的activity就是singleTask模式的),但启动singleTask模式的Activity会导致任务栈内它上面的Activity被销毁,有可能会影响用户体验,使用时要注意。
4、android:launchMode=”singleInstance” 单例启动模式,singleInstance模式也是单例的,但和singleTask不同,singleTask只是任务栈内单例,系统里是可以有多个singleTask Activity实例的,而singleInstance Activity在整个系统里只有一个实例,启动一singleInstanceActivity时,系统会创建一个新的任务栈,并且这个任务栈只有他一个Activity。比如设置B为这种模式, 那么当依次启动A-B-C-D(此时D在栈顶),此时原始的栈中就有A、C、D这三个Activity,而B在一个新的栈中。此时当你按back键,你会发现是这样子的,D-C-A–B,这是因为DCA在先创建的(也就是主栈)中,所以当主栈中的Activity全部都移除栈外后, 才会轮到次栈,这个次栈中只有一个B。
把Activity独立于一个栈中,是为了别的程序访问此Activity,可以方便多个应用程序共享这个栈中的Activity。SingleInstance模式并不常用,如果我们把一个Activity设置为singleInstance模式,你会发现它启动时会慢一些,切换效果不好,影响用户体验。它往往用于多个应用之间,例如一个电视launcher里的Activity,通过遥控器某个键在任何情况可以启动,这个Activity就可以设置为singleInstance模式,当在某应用中按键启动这个Activity,处理完后按返回键,就会回到之前启动它的应用,不影响用户体验。
设置此状态,记住以下原则,首先会查找是否存在和被启动的Activity具有相同的亲和性的任务栈(即taskAffinity,注意同一个应用程序中的activity的亲和性一样,所以下面的a情况会在同一个栈中,前面这句话有点拗口,请多读几遍),如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的activity顺序不变,如果没有,则新建一个栈来存放被启动的activity
a. 前提: Activity A和Activity B在同一个应用中.
操作: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B, 启动Activity B的Intent的Flag设为
FLAG_ACTIVITY_NEW_TASK, Activity B被压入Activity A所在堆栈(堆栈状态: AB).
原因: 默认情况下同一个应用中的所有Activity拥有相同的关系(taskAffinity).
b. 前提: Activity A在名称为"TaskOne应用"的应用中, Activity C和Activity D在名称为"TaskTwo应用"的应用中.
操作1: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity C, 启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,
Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 长按Home键, 选择TaskA,
Activity A回到前台, 再次启动Activity C(两种情况1.从桌面启动;2.从Activity A启动,两种情况一样), 这时TaskB回到前台, Activity C显示, 供用户使用, 即:
包含FLAG_ACTIVITY_NEW_TASK的Intent启动Activity的Task正在运行, 则不会为该Activity创建新的Task,
而是将原有的Task返回到前台显示.
操作2: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity C,启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,
Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 在Activity C中启动
Activity D(TaskB的状态: CD) 长按Home键, 选择TaskA, Activity A回到前台, 再次启动Activity C(从桌面或者ActivityA启动,也是一样的),
这时TaskB回到前台, Activity D显示,供用户使用.说明了在此种情况下设置FLAG_ACTIVITY_NEW_TASK后,会先查找是不是有Activity C存在的栈,
根据亲和性(taskAffinity),如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的顺序不变
从字面意思上看是要将栈中所启动activity之上的所有activity清除,通过此flag启动时还是按照默认在本栈上启动,清除上面的activity,但是即使所启动的activity已存在,仍会销毁重新创建,如果结合single top 的flag则不会重新创建。
前提: Activity A, Activity B, Activity C和Activity D在同一个应用中.
操作: Activity A启动开僻Task堆栈(堆栈状态: A), 在Activity A中启动Activity B(堆栈状态: AB), 在Activity B中启动
Activity C(堆栈状态: ABC), 在Activity C中启动Activity D(堆栈状态: ABCD), 在Activity D中启动Activity B,
启动Activity B的Intent的Flag设置为FLAG_ACTIVITY_CLEAR_TOP, (堆栈状态: AB).
必须配合new task使用,表示新启动的activity作为新栈的根activity。
前提: Activity A在名称为"TaskOne应用"的应用中, Activity C和Activity D在名称为"TaskTwo应用"的应用中.
操作: 在Launcher中单击"TaskOne应用"图标, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity C,启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,
Android系统会为Activity C开僻一个新的Task, 命名为TaskB(TaskB堆栈状态: C), 在Activity C中启动
Activity D(TaskB的堆栈状态: CD), 长按Home键, 选择TaskA, Activity A回到前台, 在Activity A中再次启动Activity C,
在启动Activity C的Intent中设置Flag为FLAG_ACTIVITY_BROUGHT_TO_FRONT, TaskB回到前台,
Activity C显示, (TaskB的堆栈状态: C).
与FLAG_ACTIVITY_NEW_TASK结合使用, 首先在Intent中设置FLAG_ACTIVITY_NEW_TASK, 打开Activity,则启动一个新Task, 接着在Intent中设置FLAG_ACTIVITY_MULTIPLE_TASK, 再次打开同一个Activity,则还会新启动一个Task.
当前Task堆栈中存在ABCD四个Activity, A是栈顶Activity, D为栈底Activity, 存在打开A的Inten中设置了FLAG_ACTIVITY_SINGLE_TOP标志, 则会使用栈顶A, 而不会从新New A.
一般与FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET结合使用,如果设置该属性,这个activity将在一个新的task中启动或者或者被带到一个已经存在的task的顶部,这时这个activity将会作为这个task的首个页面加载。将会导致与这个应用具有相同亲和力的task处于一个合适的状态(移动activity到这个task或者从中移出),或者简单的重置这个task到它的初始状态
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET:在当前的Task堆栈中设置一个还原点,当带有FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent请求启动这个堆栈时(典型的例子是用户从桌面再次启动这个应用),还原点之上包括这个应用将会被清除。应用场景:在email程序中预览图片时,会启动图片观览的actvity,当用户离开email处理其他事情,然后下次再次从home进入email时,我们呈现给用户的应该是上次email的会话,而不是图片观览,这样才不会给用户造成困惑。
例: 存在Activity A, Activity B, Activity C, Activity A启动开僻Task堆栈, 命名为TaskA(TaskA堆栈状态: A),
在Activity A中启动Activity B(TaskA堆栈状态: AB), 接着Activity B启动Activity C(TaskA堆栈状态: ABC),
启动Activity C的Intent中设置FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET标题, 这样TaskA中有一个还原点,
当有包含FLAG_ACTIVITY_RESET_TASK_IF_NEEDED的Intent请求TaskA堆栈时(比如请求Activity A)
系统就会将还原点以上的Activity清除, TaskA堆栈中只剩下了AB.
(1) taskAffinity属性应和FLAG_ACTIVITY_NEW_TASK标志及allowTaskReparenting属性结合使用, 如果只使用taskAffinity属性,请参考上面Task默认的行为.
(2) 与FLAG_ACTIVITY_NEW_TASK标志结合:
a. 前题: Activity A和Activity B在同一个应用中, Activity A与Activity B设置不同的taskAffinity属性.
操作: Activity A启动开僻Task堆栈,命名为TaskA(TaskA堆栈状态: A), 在Activity A中启动Activity B, 启动Activity B
的Intent中设置FLAG_ACTIVITY_NEW_TASK标志,这时系统会新开僻一个Task堆栈,TaskB(TaskB堆栈状态: B).
b. 前题: Activity A在"TaskOne应用"中, Activity C在"TaskTwo应用"中, Activity A和ActivityC设置了相同的taskAffinity属性.
操作: Activity A启动开僻Task堆栈,命名为TaskA(TaskA堆栈状态: A), 在Activity A中启动Activity C, 启动Activity C的
Intent中设置FLAG_ACTIVITY_NEW_TASK标志,这时Activity C会压入与Activity A堆栈相同的TaskA堆栈(TaskA堆栈状态: AC).
(3) 与allowTaskReparenting属性:
例: 在"TaskOne应用"中有一个天气预报Activity A, Activity A与"TaskOne应用"中的其它Activity有默认的关系
(taskAffinity属性都没有设置), 并且allowTaskReparenting属性设置为true, 现在存在一个"TaskTwo应用
"启动了"TaskOne应用"中的天气预报Activity A, 这时Activity A与"TaskTwo应用"中的Activity在同一个Task,
命名这个Task堆栈为TaskA, 这时"TaskOne应用"启动, 并且又打开发天气预报Activity A, 这时Activity A会从TaskA堆栈中转移到
"TaskOne应用"所在的堆栈, 即Activity A可以在多个堆栈中来回转移.
如果Task堆栈中的Root Activity设置了此属性值为true, 不管出现任何情况, 一直会保留Task栈中Activity的状态.
如果Task堆栈中的Root Activity设置了此属性值为true, 只要你一离开这个Task栈, 则系统会马上清理除了Root Activity的全部Activity.
如果某Activity设置了finishOnTaskLaunch属性, 只要你一离开这个Task栈, 则系统会马上清除这个Activity,不管这个Activity在堆栈的任何位置.
正因为activity的很多启动模式或flag具有清除栈内已有activity的效果,清除实际是调用的系统的remove task方法,该方法会使得被清除的activity 执行onDestory等方法销毁,同时如果被销毁的activity是被其他activity 用startActivityForResult方法启动的,销毁时会给它的启动activity传递回去result cancel 事件,启动activity 的onActivityResult方法会被调用,这个时候容易出现我们意料之外的问题。
例如:A是一个桌面应用的入口 activity,并设为了singleTask 启动模式的,我在A中用startActivityForResult方法启动了B,其中有个逻辑是如果在A的的onActivityResult方法中回调了 Result cancel事件,A activity调用finish销毁。首次点击A,然后在A中用startActivityForResult启动B,这里没问题,然后home键回桌面,再次点击应用图标启动,此时发现没有反应什么界面都没出现,看现象还以为应用异常闪退。实际是由于A是singleTask 启动模式,点图标启动A时要清除A所在栈上面的所有activity,栈中B被remove task 然后onDestory, 栈中原来的A收到onActivityResult事件,然后执行finish也销毁了,然后就因为没有activity存在了不显示任何界面。
从Task的角度看,Android认为不同Task之间的Activity是不能传递数据的,因此如果启动用startActivityForResult方式启动新activity,而新启动的activity和当前activity不在同一个栈时,当前activity的onActivityResult方法回马上被回调,并且传递回result cancel事件,可以从AMS打出的log上看到有这么一句:
“WARN/ActivityManager(67): Activity is launching as a new task, so cancelling activity result.” 就是这个意思,下面startActivityForResult的注释也进行了说明。
因此对于启动模式是singleInstance或singleTask,或者是new task flag标志启动的情况要尤其注意下此问题。