LaunchMode
1、standard
standard启动模式为最基本的启动模式,默认为该种启动模式,特点就是每当发送一个intent请求打开该activity时,都会创建一个新的activity实例。实际使用情况分为两种,一种是本应用打开,一种是跨应用打开:
-本应用打开,新创建的activity实例放入本应用,即发送intent的task栈的顶部
-跨引用打开,这里有一个需要注意的地方是跨应用打开的时候会在 Recent app 页面显示两个独立项,但是此时它们两个 Activity 仍然是在一个栈中
如想要保持在一个recent app可以设置启动启动intent的flag包含:
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
此时两个Activity仍在同一栈中。
2、singleTop
singleTop其实和standard几乎一样,和standard算一组,使用singleTop的Activity也可以创建很多个实例。唯一不同的就是,如果调用的目标Activity已经位于调用者的Task的栈顶,则不创建新实例,而是使用当前的这个Activity实例,并调用这个实例的onNewIntent方法。
这个使用场景比较少,可以使用的例子比如用户已经在当前activity,用户点击一条推送消息之后也需要跳转到当前activity,那么为了避免activity的重复打开,则需要将该activity设置为singleTop并且复写onNewIntent即可。
如果是外部程序启动singleTop的Activity,在Android 5.0之前新创建的Activity会位于调用者的Task中,5.0及以后会放入新的Task中,这点和standard一样。
3、singleTask
使用singleTask启动模式的Activity在系统中只会存在一个实例。如果这个实例已经存在,intent就会通过onNewIntent传递到这个Activity,并且将栈中该activity之上的activity清除(销毁过程会调用Activity生命周期回调),如果不存在新的Activity实例将被创建。
实际使用情况也分为两组:
-本应用启动,在一个应用中启动设置为singleTask的activity,如果该activity在task栈中不存在,则会创建一个新的实例放在栈顶,如果在activity的task栈中已经存在了该activity实例,则会将栈中该activity实例之上的其他activity实例清空,并且会调用该activity的onNewIntent方法。最常用的使用例子就是首页。
使用提示:此时onNewIntent方法中不能进行fragment的相关操作,否则IllegalStateException: Can not perform this action after onSaveInstanceState,可在OnResume中根据intent再进行相关操作。
-跨应用启动,由于整个系统只能存在activity的一个实例,所以如果系统中不存在该activity,则会启动一个新的task去启动该activity,并且将该activity放入栈底。如果系统中存在该activity实例,则会直接启动该activity,调用该activity的onNewIntent的方法,同时将该activity task栈上面的其他activity清空(销毁过程会调用Activity生命周期回调),此时如果用户摁下返回键,那么将在singleTask activity的task栈中操作,即返回该栈中singleTask activity的上一个activity直到该栈中无activity时才会返回到最开始启动singleTask activity的activity中。还有一种情况是singleTask Activity所在的应用进程存在,但是singleTask Activity实例不存在,那么从别的应用启动这个Activity,新的Activity实例会被创建,并放入到所属进程所在的Task中,并位于栈顶位置。注意如果使用了singleTask,FLAG_ACTIVITY_RESET_TASK_IF_NEEDED这个flag将会失效。
需要特别注意的是:
1.在4.x和之前的系统下,A1(startActivityForResult)->A2(singleTask, startActivityForResult)->A3->A4,当A1打开A2之后会立即回调onActivityResult()函数,返回RESULT_CANCELED,
Logcat输出如下的信息:
Activity is launching as a new task, so cancelling activity result
A2打开A3仍然可以正常回调onActivityResult();但是从5.0开始,A1打开A2的时候 onActivityResult() 函数也能正常的回调,不会立即回调。
4、singleInstance
和singleTask类似,在系统中只会存在一个实例,唯一的区别就是系统不会在singleInstance activity的task栈中启动任何其他的activity,singleInstance activity栈中仅仅只能有该activity的实例,其他任何从这个activity启动的activity都会在其他的栈中被打开。虽然使用adb shell dumpsys activity可以看到singleInstance activity在一个独立的task中,但是在任务管理器中,还是显示的一个。
需要特别注意的是:
1)、A1->A2(SingleInstance),摁下 Home 键之后,点击应用图标再次进入应用,返回的是 A1 页面,这是因为 A2 在另一个单独的 Activity task 栈中,点击图标返回的是主 Activity栈(根据taskAffinity),所以此时显示的 A1 页面,而不是 A2 页面。
2)、注意singleInstance的返回键的处理和上面3个 mode 有区别,如果是使用正常的 startActivity 进行的启动,启动顺序A1->A2->A3(singleInstance)->A4,在A4页面摁下back键,返回的是A2,再返回A1,接着再次摁下back键,返回的才是A3;如果是使用 startActivityForResult 启动的,在4.x和之前的系统下,表现和之前是一样的,但是在5.0开始,A1->A2-A3(singleInstance + startActivityForResult)->A4,在A4摁下返回键,返回的是A3->A2->A1,这个需要着重说明一下。
3.另一个比较重要的是,在4.x和之前的系统下,A1(startActivityForResult)->A2(singleInstance, startActivityForResult)->A3->A4,当A1打开A2之后会立即回调onActivityResult()函数,情况类似singleTask,A2打开A3也会立即回调onActivityResult()函数;但是从5.0开始,A1打开A2和A2打开A3的两种情况下 onActivityResult() 函数都能正常的回调,不会立即回调。
IntentFlag
FLAG_ACTIVITY_BROUGHT_TO_FRONT
比方说我现在有A,在A中启动B,在A中Intent中加上这个标记。此时B就是以FLAG_ACTIVITY_BROUGHT_TO_FRONT 这个启动的,在B中再启动C,D(正常启动C,D),如果这个时候在D中再启动B,这个时候最后的栈的情况是 A,C,D,B。
FLAG_ACTIVITY_REORDER_TO_FRONT
如果在Intent中设置,并传递给Context.startActivity(),这个标志将引发已经运行的Activity移动到历史stack的顶端。 例如,假设一个Task由四个Activity组成:A,B,C,D。如果D调用startActivity()来启动Activity B,那么,B会移动到历史stack的顶端,现在的次序变成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP标志也设置的话,那么这个标志将被覆盖。
如果在调用startActivity时传递这个标记,该task栈中的其他activity会先被清空,然后该activity在该task中启动,也就是说,这个新启动的activity变为了这个空task的根activity。所有老的activity都结束掉。该标志必须和FLAG_ACTIVITY_NEW_TASK一起使用。
如果该activity已经在task中存在,并且设置了该task,系统不会启动新的 Activity 实例,会将task栈里该Activity之上的所有Activity一律结束掉,然后将Intent发给这个已存在的Activity。Activity收到 Intent之后,或者在onNewIntent()里做下一步的处理,或者自行结束然后重新创建。如 Activity 在 AndroidMainifest.xml 里将启动模式设置成默认standard模式,且 Intent 里也没有设置 FLAG_ACTIVITY_SINGLE_TOP,那么Activity将会结束并且重启;否则则会传递到onNewIntent方法。
已经启动了四个Activity:A,B,C和D。在D Activity里,我们要跳到B Activity,同时希望C finish掉,可以在startActivity(intent)里的intent里添加flags标记,这样启动B Activity,就会把D,C都finished掉,如果你的B Activity的启动模式是默认的(multiple) ,则B Activity会finished掉,再启动一个新的Activity B。 如果不想重新再创建一个新的B Activity,则可在启动Intent添加flag FLAG_ACTIVITY_SINGLE_TOP。
可以利用此特性来退出程序,假设A为程序入口,将A的Manifest.xml配置成android:launchMode="singleTop",通过此flag来启动A,同时在A的onNewIntent中判断来结束自己,可到退出的效果。FLAG_ACTIVITY_CLEAR_TOP 还可以和 FLAG_ACTIVITY_NEW_TASK 配合使用,用来启动一个task栈的根activity,他将会把该栈清空为根状态,比如从notification manager启动activity。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
设置完之后,新的activity将不会添加到当前activity列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。他等同于在XML中指定Activity的属性android:excludeFromRecents=”true”
如果A需要onActivityResult中获取返回结果,startActivityForResult B,而B只是过渡页,启动C之后就finish掉了,需要在 C 中setResult返回给A就可以用到这个标志。
A -> B -> XXXXX(无论多少个过渡页) 设置 FLAG_ACTIVITY_FORWARD_RESULT 来启动 C ,之后该XXX过渡页finish - > C ,那么C的结果返回给A。
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。例如A启动B的时候,给B设置了FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY,那么:
A -> B -> C ,启动C 就算 B没有自行finish ,也会变为 AC
这个标识用来创建一个新的task栈,并且在里面启动新的activity(所有情况,不管系统中存在不存在该activity实例),经常和FLAG_ACTIVITY_NEW_DOCUMENT或者FLAG_ACTIVITY_NEW_TASK一起使用。这上面两种使用场景下,如果没有带上FLAG_ACTIVITY_MULTIPLE_TASK标识,他们都会使系统搜索存在的task栈,去寻找匹配intent的一个activity,如果没有找到就会去新建一个task栈;但是当和FLAG_ACTIVITY_MULTIPLE_TASK一起使用的时候,这两种场景都会跳过搜索这步操作无条件的创建一个新的task。和FLAG_ACTIVITY_NEW_TASK一起使用需要注意,尽量不要使用该组合除非你完成了自己的顶部应用启动器,他们的组合使用会禁用已经存在的task栈回到前台的功能。
api 21之后加入的一个标识,用来在intent启动的activity的task栈中打开一个document,和documentLaunchMode效果相等,有着不同的documents的activity的多个实例,将会出现在最近的task列表中。
documentLaunchMode可以设置4个值
intoExisting: activity 会为该document请求一个已经存在的task,这与设置FLAG_ACTIVITY_NEW_DOCUMENT且不设置FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果。
always: activity 会为该document创建一个新的task,即使该document已经被打开了,这与设置 FLAG_ACTIVITY_NEW_DOCUMENT且设置 FLAG_ACTIVITY_MULTIPLE_TASK 有相同的效果。
none:activity 不会为 document 创建新的task,该app被设置为 single task 的模式,它会重新调用用户唤醒的所有activity中的最近的一个。
never:activity 不会为document创建一个新的task,设置这个值复写了 FLAG_ACTIVITY_NEW_DOCUMENT 和 FLAG_ACTIVITY_MULTIPLE_TASK 标签。如果其中一个标签被设置,并且overview screen 显示该app为 single task 模式。则该activity会重新调用用户最近唤醒的activity。
注意: none 或 nerver 使用时,activity必须设置为 launchMode=”standard” ,如果该属性没有设置,documentLaunchMode=”none” 属性就会被使用。有
FLAG_ACTIVITY_RETAIN_IN_RECENTS
api21加入。默认情况下通过FLAG_ACTIVITY_NEW_DOCUMENT启动的activity在关闭之后,task中的记录会相对应的删除。如果为了能够重新启动这个activity你想保留它,就可以使用者个flag,最近的记录将会保留在接口中以便用户去重新启动。接受该flag的activity可以使用autoRemoveFromRecents去复写这个request或者调用Activity.finishAndRemoveTask()方法。
设置此状态,记住以下原则,首先会查找是否存在和被启动的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调用onUserLeaveHint()函。onUserLeaveHint()作为activity周期的一部分,它在activity因为用户要跳转到别的activity而退到background时使用。比如,在用户按下Home键(用户的操作),它将被调用。比如有电话进来(不属于用户的操作),它就不会被调用。注意:通过调用finish()时该activity销毁时不会调用该函数。
如果给Intent对象设置了这个标记,这个Intent对象被用于从一个存在的Activity中启动一个新的Activity,那么新的这个Activity不能用于接受发送给顶层activity的intent,这个新的activity的前一个activity被作为顶部activity。
api11加入。把当前新启动的任务置于Home任务之上,也就是按back键从这个任务返回的时候会回到home,即使这个不是他们最后看见的activity,注意这个标记必须和FLAG_ACTIVITY_NEW_TASK一起使用。
FLAG_EXCLUDE_STOPPED_PACKAGES和FLAG_INCLUDE_STOPPED_PACKAGES
从Android 3.1开始,给Intent定义了两个新的Flag,分别为FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,用来控制Intent是否要对处于停止状态的App起作用,顾名思义:
FLAG_INCLUDE_STOPPED_PACKAGES:表示包含未启动的App
FLAG_EXCLUDE_STOPPED_PACKAGES:表示不包含未启动的App
值得注意的是,Android 3.1开始,系统向所有Intent的广播添加了FLAG_EXCLUDE_STOPPED_PACKAGES标志。这样做是为了防止广播无意或不必要地开启未启动App的后台服务。如果要强制调起未启动的App,后台服务或应用程序可以通过向广播Intent添加FLAG_INCLUDE_STOPPED_PACKAGES标志来唤醒。
taskAffinity
每个Activity都有taskAffinity属性,这个属性指出了它希望进入的Task。如果一个Activity没有显式的指明该 Activity的taskAffinity,那么它的这个属性就等于Application指明的taskAffinity,如果 Application也没有指明,那么该taskAffinity的值就等于包名。而Task也有自己的affinity属性,它的值等于它的根 Activity的taskAffinity的值。
taskAffinity属性主要和singleTask启动模式或者allowTaskReparenting属性配对使用,在其他情况下没有意义。当taskAffinity和singleTask启动模式配对使用的时候,他是具有该模式的Activity的目前任务栈的名字,待启动的Activity会运行在名字和TaskAffinity相同的任务栈中。allowTaskReparenting用于配置是否允许该activity可以更换从属task,通常情况二者连在一起使用,用于实现把一个应用程序的Activity移到另一个应用程序的Task中。allowTaskReparenting用来标记Activity能否从启动的Task移动到taskAffinity指定的Task,默认是继承至application中的allowTaskReparenting=false,如果为true,则表示可以更换;false表示不可以。
如果加载某个Activity的intent,Flag被设置成FLAG_ACTIVITY_NEW_TASK时,它会首先检查是否存在与自己taskAffinity相同的Task,如果存在,那么它会直接宿主到该Task中,如果不存在则重新创建Task。