任务栈 Activity的启动模式 Intent中的Flag taskAffinity

Posted 白乾涛

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了任务栈 Activity的启动模式 Intent中的Flag taskAffinity相关的知识,希望对你有一定的参考价值。


关于任务栈Task
栈的概念
(Stack)是一种常用的数据结构,栈只允许访问栈顶的元素,栈就像一个杯子,每次都只能取杯子顶上的东西,而对于栈就只能每次访问它的栈顶元素,从而可以达到保护栈顶元素以下的其他元素.”先进后出”或”后进先出”就是栈的一大特点,先进入栈的元素总是要等到后进入栈的元素出栈以后才能出栈.递归就是利用到了系统栈,暂时保存临时结果,对临时结果进行保护.
栈的基本操作:压栈、弹栈

任务栈
Task简单的就是一组以栈的模式聚集在一起的Activity组件集合,类似于一个填充了Activity的容器,最先加入的Activity会处于容器最下面,最后加入的处于容器最上面,而从Task中取出Activity时是从最顶端先取出,最后取出的是最开始添加Activity,这就是后进先出模式,而Activity在Task中的顺序是可以控制的,在Activity跳转时用到Intent Flag可以设置新建activity的创建方式

Activity的启动模式
Activity启动模式有四种,可以根据实际的需求为Activity设置对应的启动模式,从而可以避免创建大量重复的Activity等问题。
设置Activity的启动模式,只需要在androidManifest.xml里对应的<activity>标签设置Android:launchMode属性。

standard
默认模式,可以不用写配置。在这个模式下,都会默认创建一个新的实例。因此,在这种模式下,可以有多个相同的实例,也允许多个相同Activity叠加。
例如:若我有一个Activity名为A1, 上面有一个按钮可跳转到A1。那么如果我点击按钮,便会新启一个Activity A1叠在刚才的A1之上,再点击,又会再新启一个在它之上。点back键会依照栈顺序依次退出。

singleTop
可以有多个实例,但是不允许多个相同Activity叠加。即,如果Activity在栈顶的时候,启动相同的Activity,不会创建新的实例,而会调用其onNewIntent方法。

singleTask
只有一个实例。
如果在同一个应用程序中启动它,若Activity不存在,则会在当前task创建一个新的实例,若存在,则会把task中在其之上的其它Activity destory掉并调用它的onNewIntent方法。
如果是在别的应用程序中启动它,则会新建一个task,并在该task中启动这个Activity。
singleTask允许别的Activity与其在一个task中共存,也就是说,如果我在这个singleTask的实例中再打开新的Activity,这个新的Activity还是会在singleTask的实例的task中。
可以使用singleTask来退出整个应用。
将主Activity设为SingTask模式,然后在要退出的Activity中转到主Activity,然后重写主Activity的onNewIntent函数,并在函数中加上一句finish。

singleInstance
只有一个实例,并且这个实例独立运行在一个task中,这个task只有这个实例,不允许有别的Activity存在。

Intent中常用的Flag
Flag表示Intent的标志位,常用于Activity的场景中,它和Activity的启动模式有着密切的联系。
下面列举的是和Activity启动模式相关的Flag属性:

Intent.FLAG_ACTIVITY_NEW_TASK
系统会检查当前所有已创建的Task中是否有该要启动的Activity的Task,若有,则在该Task上创建Activity;若没有则新建具有该Activity属性的Task,并在该新建的Task上创建Activity。

FLAG_ACTIVITY_SINGLE_TOP
这个FLAG就相当于启动模式中的singletop
比如说原来栈中情况是ABCD,在D中启动D,栈中的情况还是ABCD

FLAG_ACTIVITY_CLEAR_TOP
这个FLAG就相当于启动模式中的SingleTask,这种FLAG启动的Activity会把要启动的Activity之上的Activity全部弹出栈空间。
比如:原来栈中的情况是ABCD,这个时候从D中跳转到B,这个时候栈中的情况就是AB了

FLAG_ACTIVITY_NO_HISTORY
这个标记顾名思义意思就是说,用这个FLAG启动的Activity,一旦【不可见】,他就不会存在于任务栈中。
比如,原来是ABC,这个时候在C中以这个FLAG启动D的 ,D再启动E,这个时候栈中情况为ABCE。

FLAG_ACTIVITY_BROUGHT_TO_FRONT
比方说我现在在A中启动B,启动时在A中的Intent加上这个标记,此时在B中再启动CD(正常启动CD),此时栈的情况是ABCD。
如果这个时候在D中再启动B,这个时候栈的情况是 ACDB。
特别注意的是,我上面说的网上人描述的这个FLAG,会很容易让人误解成这样,A,B,C,D都是标准加载,然后我在D中启动A,这个intent加上FLAG_ACTIVITY_BROUGHT_TO_FRONT  ,就会误认为变成B,C,D,A!!其实不是,这个时候应该是A,B,C,D,A.不信的人大家试试看。不过下面这个标记和这个标记就会让大家明白了!

FLAG_ACTIVITY_REORDER_TO_FRONT
就按在 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT 最后说的,如果在A,B,C,D正常启动的话,不管B有没有用FLAG_ACTIVITY_BROUGHT_TO_FRONT启动,此时在D中启动B的话,还是会变成A,C,D,B的。

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
这个也不知道具体怎么用,看文档有点象开辟新的Task,不过测试完,不像那么简单。

FLAG_ACTIVITY_NO_USER_ACTION
onUserLeaveHint()作为activity周期的一部分,它在activity因为用户要跳转到别的activity而要退到background时使用。
比如,在用户按下Home键(用户的choice),它将被调用。比如有电话进来(不属于用户的choice),它就不会被调用。
那么系统如何区分让当前activity退到background时使用是用户的choice?
它是根据促使当前activity退到background的那个新启动的Activity的Intent里是否有     FLAG_ACTIVITY_NO_USER_ACTION来确定的。
注意:通过调用finish()使该activity销毁时不会调用该函数

Intent中所有定义的Flag
FLAG_GRANT_READ_URI_PERMISSION  //
FLAG_GRANT_WRITE_URI_PERMISSION  //
FLAG_FROM_BACKGROUND  //
FLAG_DEBUG_LOG_RESOLUTION  //
FLAG_EXCLUDE_STOPPED_PACKAGES  //
FLAG_INCLUDE_STOPPED_PACKAGES  //
FLAG_ACTIVITY_NO_HISTORY  //退出后不会存在于任务栈中
FLAG_ACTIVITY_SINGLE_TOP  //单一顶部
FLAG_ACTIVITY_NEW_TASK  //在新任务栈中打开
FLAG_ACTIVITY_MULTIPLE_TASK  //
FLAG_ACTIVITY_CLEAR_TOP  //若此Activity已存在于任务栈中,再次启动时弹出它所有顶部的Activity
FLAG_ACTIVITY_FORWARD_RESULT  //
FLAG_ACTIVITY_PREVIOUS_IS_TOP  //
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS  //
FLAG_ACTIVITY_BROUGHT_TO_FRONT  //
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED  //
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY  //
FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET  //
FLAG_ACTIVITY_NO_USER_ACTION  //
FLAG_ACTIVITY_REORDER_TO_FRONT  //
FLAG_ACTIVITY_NO_ANIMATION  //
FLAG_ACTIVITY_CLEAR_TASK  //
FLAG_ACTIVITY_TASK_ON_HOME  //
FLAG_RECEIVER_REGISTERED_ONLY  //
FLAG_RECEIVER_REPLACE_PENDING  //
FLAG_RECEIVER_FOREGROUND  //
FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT  //
FLAG_RECEIVER_BOOT_UPGRADE  //

关于taskAffinity属性
清单文件中,activity 的属性android:allowTaskReparenting用于设定:Activity是否能够从启动它的任务栈中转移到另一个与启动它的任务栈有相同taskAffinity属性值的任务栈,转移时机是在另一个任务栈被带到前台的时候。如果设置为true,则能够转移,如果设置了false,则这个Activity必须要保留在启动它的那个任务栈中。

实验
1、新建两个工程,App1和App2
App1和App2都设置android:taskAffinity="aaa.aaa"android:allowTaskReparenting="true"
先运行App1,然后点击home键,让App1运行在后台
再运行App2,会发现这时显示的是App1的mainActivity,并且长按home键,会发现运行过的程序只有App1。

2、紧接着又在此基础上做了另外一个实验
在App1上新建一个secondActivity,设置android:taskAffinity="aaa.bbb"android:allowTaskReparenting="true"
在mainActivity中startActivity时,设置Intent中flag属性为FLAG_ACTIVITY_NEW_TASK,注意,这里的flag属性必须为newtask。
然后运行App1,点击进入secondActivity,这时长按home键,会发现运行过的程序中有两个App1,并且一个显示的是mainActivity另一个显示的是secondActivity。
这时点击home键,让程序回到后台
然后运行App2,会发现这时显示的是App1的mainActivity,此时点击返回会直接返回home。
注意,虽然此时App1的mainActivity被finish了,但App1的secondActivity还在后台等着呢。
同样,长按home键,会发现运行过的程序只有App1的两个任务栈,并没有App2。

3、在此基础上堆App1再次修改
在App1上新建一个thirdActivity,设置属性android:taskAffinity="aaa.aaa"android:allowTaskReparenting="true"
并在secondActivity中startActivity时,设置Intent中flag属性为FLAG_ACTIVITY_NEW_TASK;
运行App1,点击进入secondActivity,再进入thirdActivity,此时长按home键,会发现运行过的程序中有两个App1,并且一个显示的是secondActivity另一个显示的是thirdActivity。
此时点击返回,会回到mainActivity,再点击返回,会回到secondActivity,再点击返回,回到home页面。

以上实验中,如果startActivity时不设置Intent中flag属性为FLAG_ACTIVITY_NEW_TASK,则若先启动的是App1,那么不管是App1中的Activity还是App2中的mainActivity,都是运行App1的一个任务栈中。





以上是关于任务栈 Activity的启动模式 Intent中的Flag taskAffinity的主要内容,如果未能解决你的问题,请参考以下文章

任务栈 Activity的启动模式 Intent中的Flag taskAffinity

activity调用栈

Android基础Activity启动模式以及Intent Flags 与 栈 的全面解析

Android 中Activity和Intent的详解

Activity的启动模式

Android Activity的任务栈和四大启动模式