Activity启动模式(三)之 Flag

前言

前文 Activity启动模式(一)之 launchMode 介绍了通过配置清单文件launchMode的方式来改变Activity启动模式

前文 Activity启动模式(二)之 taskAffinity 介绍了taskAffinity属性的特点

本篇将介绍修改Activity启动模式的另一种方式:代码内配置Intent.setFlags()形式。 代码内配置启动模式优先级高于清单文件launchMode模式。

Intent Flag

代码内配置启动方式的代码格式如下: 其中

  • context 上下文
  • FlagNewTaskClearTopActivity 需要启动的Activity
  • setFlags 配置启动模式,用“|”符号(位或运算符)累加Flag
    1
    2
    startActivity(new Intent(context, FlagNewTaskClearTopActivity.class)
    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP));

点击查看:Flag测试源码

Intent的Flag有很多,就挑一些介绍一下吧。

FLAG_ACTIVITY_CLEAR_TASK

如果在调用 Context.startActivity()时传递这个标记,该activity在启动前,会将该activity所属task栈中的其他activity清空,也就是说,这个新启动的activity变为了这个空task的根activity。所有老的activity都结束掉。该标志必须和FLAG_ACTIVITY_NEW_TASK一起使用。 即:Intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)

FLAG_ACTIVITY_CLEAR_TOP

如果该activity已经在当前task中存在,并且设置了该flag,系统不会启动新的 Activity 实例,会将task栈里将该Activity之上的所有Activity一律结束掉,然后将Intent发给这个已存在的Activity。Activity收到 Intent之后,或者在onNewIntent()里做下一步的处理,或者自行结束然后重新创建自己。

  • AndroidMainifest 为standard模式,且FLAG_ACTIVITY_CLEAR_TOP:清除栈内该Activity和该Activity上面的所有Activity,并重新实例化该Activity置入栈顶
  • FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP :效果和singleTask一样,清除栈内该Activity上面的所有Activity,并重用该Activity,在onNewIntent()里做下一步的处理
  • FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_NEW_TASK:根据taskAffinity属性找到或新建对应的栈,之后同第一种情况一样

FLAG_ACTIVITY_NEW_TASK  

设置此状态,记住以下原则,首先会查找是否存在和被启动的Activity具有相同的亲和性的任务栈(即taskAffinity,注意同一个应用程序中的activity的亲和性在没有修改的情况下是一样的,所以下面的a情况会在同一个栈中),如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的activity顺序不变,如果没有,则新建一个栈来存放被启动的activity。  

a. 前提: Activity LearnMainActivity(以下用Activity A 替代说明)和Activity 启动模式-Flag NEW_TASK (以下用Activity B 替代说明)在同一个应用中。

操作:

  1. Activity A启动开僻Task堆栈(堆栈状态:A),

  2. 在Activity A中启动Activity B, 启动Activity B的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,Activity B被压入Activity A所在堆栈(堆栈状态:AB),

  3. 在Activity B中再次启动Activity B, 启动Activity B的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,Activity B被压入Activity A所在堆栈(堆栈状态:ABB),

    原因: 默认情况下同一个应用中的所有Activity拥有相同的关系(taskAffinity),此时的Activity B同standard启动模式一致。

b. 前提: Activity LearnMainActivity(以下用Activity A 替代说明)在名称为”LearnApplication”的应用中, Activity MainActivity (以下用Activity C 替代说明)和Activity (以下用Activity D 替代说明)在名称为”AppB”的应用中。

操作1:

  1. 在Launcher中单击“LearnApplication”图标,Activity A启动开僻Task堆栈,命名为TaskA(TaskA堆栈状态: A),
  2. 在Activity A中启动Activity C, 启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,Android系统会为Activity C开僻一个新的Task,命名为TaskB(TaskB堆栈状态: C),
  3. 长按Home键,选择TaskA,Activity A回到前台,
  4. 再次启动Activity C(两种情况:1.从桌面启动;2.从Activity A启动,两种情况一样), 这时TaskB回到前台, Activity C显示,供用户使用,
    即:包含FLAG_ACTIVITY_NEW_TASK的Intent启动Activity的Task正在运行,则不会为该Activity创建新的Task,而是将原有的Task返回到前台显示。   

操作2:

  1. 在Launcher中单击”LearnApplication”图标,Activity A启动开僻Task堆栈,命名为TaskA(TaskA堆栈状态: A),
  2. 在Activity A中启动Activity C,启动Activity C的Intent的Flag设为FLAG_ACTIVITY_NEW_TASK,Android系统会为Activity C开僻一个新的Task,命名为TaskB(TaskB堆栈状态: C),
  3. 在Activity C中启动Activity D(TaskB的状态: CD)
  4. 长按Home键, 选择TaskA,Activity A回到前台,
  5. 再次启动Activity C(从桌面或者ActivityA启动,也是一样的),这时TaskB回到前台, Activity D显示,供用户使用。

说明了在此种情况下设置FLAG_ACTIVITY_NEW_TASK后,会先查找是不是有Activity C存在的栈,根据亲和性(taskAffinity),如果有,刚直接把这个栈整体移动到前台,并保持栈中的状态不变,即栈中的顺序不变。

补充:同一App内情况

  • FLAG_ACTIVITY_NEW_TASK(taskAffinity默认):同standard启动模式一样
  • FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_CLEAR_TOP:根据taskAffinity属性找到或新建对应的栈,清除栈内该Activity和该Activity上面的所有Activity,并重新实例化该Activity置入栈顶
  • FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_CLEAR_TASK:根据taskAffinity属性找到或新建对应的栈,清空栈内所有Activity,将该Activity实例压入栈内,成为栈底Activity。

FLAG_ACTIVITY_SINGLE_TOP

  同singleTop一样 ,如果该Activity在栈顶,则重用该Activity,在onNewIntent()里做下一步的处理

其他一些Flag

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_CLEAR_WHEN_TASK_RESET

   api21后已经废弃,请使用FLAG_ACTIVITY_NEW_DOCUMENT

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

   设置完之后,新的activity将不会添加到当前activity列表中,当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。他等同于在XML中指定Activity的属性android:excludeFromRecents=”true”。

FLAG_ACTIVITY_FORWARD_RESULT

   如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的Activity。

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY

   一般由系统调用,比如长摁home键从历史记录中启动。

FLAG_ACTIVITY_MULTIPLE_TASK

   这个标识用来创建一个新的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栈回到前台的功能。

FLAG_ACTIVITY_NEW_DOCUMENT

   api 21之后加入的一个标识,用来在intent启动的activity的task栈中打开一个document,和documentLaunchMode效果相等,有着不同的documents的activity的多个实例,将会出现在最近的task列表中。单独使用效果和documentLaunchMode=”intoExisting”一样,如果和FLAG_ACTIVITY_MULTIPLE_TASK一起使用效果就等同于documentLaunchMode=”always”。

FLAG_ACTIVITY_NO_ANIMATION

  禁止activity之间的切换动画

FLAG_ACTIVITY_NO_HISTORY

  该Activity将不在stack中保留,用户一离开它,这个Activity就关闭了。 onActivityResult() 也就不会调用了。

FLAG_ACTIVITY_NO_USER_ACTION

  禁止activity调用onUserLeaveHint()函。onUserLeaveHint()作为activity周期的一部分,它在activity因为用户要跳转到别的activity而退到background时使用。比如,在用户按下Home键(用户的操作),它将被调用。比如有电话进来(不属于用户的操作),它就不会被调用。注意:通过调用finish()时该activity销毁时不会调用该函数。

FLAG_ACTIVITY_PREVIOUS_IS_TOP

   如果给Intent对象设置了这个标记,这个Intent对象被用于从一个存在的Activity中启动一个新的Activity,那么新的这个Activity不能用于接受发送给顶层activity的intent,这个新的activity的前一个activity被作为顶部activity。

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标志也设置的话,那么这个标志将被覆盖。   

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

   这个标记在以下情况下会生效:1.启动Activity时创建新的task来放置Activity实例;2.已存在的task被放置于前台。系统会根据affinity对指定的task进行重置操作,task会压入某些Activity实例或移除某些Activity实例。我们结合上面的FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET可以加深理解。

FLAG_ACTIVITY_RETAIN_IN_RECENTS

  api21加入。      默认情况下通过FLAG_ACTIVITY_NEW_DOCUMENT启动的activity在关闭之后,task中的记录会相对应的删除。如果为了能够重新启动这个activity你想保留它,就可以使用者个flag,最近的记录将会保留在接口中以便用户去重新启动。接受该flag的activity可以使用autoRemoveFromRecents去复写这个request或者调用Activity.finishAndRemoveTask()方法。

FLAG_ACTIVITY_TASK_ON_HOME

  api11加入。   把当前新启动的任务置于Home任务之上,也就是按back键从这个任务返回的时候会回到home,即使这个不是他们最后看见的activity,注意这个标记必须和FLAG_ACTIVITY_NEW_TASK一起使用。

FLAG_EXCLUDE_STOPPED_PACKAGESFLAG_INCLUDE_STOPPED_PACKAGES

   在3.1之后,系统的package manager增加了对处于“stopped state”应用的管理,这个stopped和Activity生命周期中的stop状态是完全两码事,指的是安装后从来没有启动过和被用户手动强制停止的应用,与此同时系统增加了2个Flag:FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES ,来标识一个intent是否激活处于“stopped state”的应用。当2个Flag都不设置或者都进行设置的时候,采用的是FLAG_INCLUDE_STOPPED_PACKAGES的效果。

FLAG_DEBUG_LOG_RESOLUTION

将log置为可用状态,如果设置了这个flag,那么在处理这个intent的时候,将会打印相关创建日志。

FLAG_FROM_BACKGROUND

   用来标识该intent的操作是一个后端的操作而不是一个直接的用户交互。

FLAG_GRANT_PERSISTABLE_URI_PERMISSION   api19添加 当和FLAG_GRANT_READ_URI_PERMISSION 和/或FLAG_GRANT_WRITE_URI_PERMISSION一起使用时,uri权限在设置重启之后依然存在直到用户调用了revokeUriPermission(Uri, int)方法,这个标识仅为可能的存在状态提供许可,接受的应用必须要调用takePersistableUriPermission(Uri, int)方法去实际的变为存在状态。

FLAG_GRANT_PREFIX_URI_PERMISSION

  api21加入。   当和FLAG_GRANT_READ_URI_PERMISSION 和/或FLAG_GRANT_WRITE_URI_PERMISSION一起使用时,uri的许可只用匹配前缀即可(默认为全部匹配)。

FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_WRITE_URI_PERMISSION

   如果设置FLAG_GRANT_READ_URI_PERMISSION这个标记,Intent的接受者将会被赋予读取Intent中URI数据的权限和lipData中的URIs的权限。当使用于Intent的ClipData时,所有的URIs和data的所有递归遍历或者其他Intent的ClipData数据都会被授权。FLAG_GRANT_WRITE_URI_PERMISSION同FLAG_GRANT_READ_URI_PERMISSION只是相应的赋予的是写权限。   一个典型的例子就是邮件程序处理带有附件的邮件。进入邮件需要使用permission来保护,因为这些是敏感的用户数据。然而,如果有一个指向图片附件的URI需要传递给图片浏览器,那个图片浏览器是不会有访问附件的权利的,因为他不可能拥有所有的邮件的访问权限。针对这个问题的解决方案就是per-URI permission:当启动一个activity或者给一个activity返回结果的时候,呼叫方可以设置Intent.FLAG_GRANT_READ_URI_PERMISSION和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION . 这会使接收该intent的activity获取到进入该Intent指定的URI的权限,而不论它是否有权限进入该intent对应的content provider。

FLAG_RECEIVER_FOREGROUND

  api16添加。   当发送广播时,允许其接受者拥有前台的优先级,更短的超时间隔。

FLAG_RECEIVER_NO_ABORT

  api19添加   如果这是一个有序广播,不允许接受者终止这个广播,它仍然能够传递给下面的接受者。

FLAG_RECEIVER_REGISTERED_ONLY

   如果设置了这个flag,当发送广播的时,动态注册的接受者才会被调用,在Androidmanifest.xml 里定义的Receiver 是接收不到这样的Intent 的。

FLAG_RECEIVER_REPLACE_PENDING

  api8添加。 如果设置了的话,ActivityManagerService就会在当前的系统中查看有没有相同的intent还未被处理,如果有的话,就由当前这个新的intent来替换旧的intent,所以就会出现在发送一系列的这样的Intent 之后,中间有些Intent 有可能在你还没有来得及处理的时候, 就被替代掉了的情况

坚持原创技术分享,您的支持是对我最大的鼓励!