Task、Back stack、taskAffinity、Activity启动模式之间的关系

         假设有一个应用程序,它有2个界面即2Activity,当程序第一次启动时,首先显示第1个界面Activity 1,然后点击第1个界面上的一个按钮启动到它的第2个界面Activity 2,此时按下返回键程序又回到Activity 1,继续按返回键程序就退出到手机主界面,程序的2Activity表现出的是一种后进先出的行为,可以认为是有一个栈结构来保存程序依次启动的每个Activity(事实也如此),当启动Activity 1时,Activity 1就被存入这个栈中,接着启动Activity 2时,Activity 2也被存入栈中,正好处于Activity 1的上面即栈顶,当按下返回键时Activity 2就从栈顶弹出并被销毁,这时Activity 1就处于栈顶并显示其内容,当再按返回键时,Activity 1也从栈顶弹出并销毁了,然后程序就退出到手机的主界面了。这个维护Activity进出的栈结构就叫做Back stack,而栈中的一系列Activity的进出称为一个Task,即Task是一个动态的概念,每个Task都有一个与之关联的Back stack。默认情况下,一个应用程序中的所有Activity都运行在同一个Task管理的Back stack中,因为默认时所有Activity都具有相同的taskAffinity,这个taskAffinity是一个字符串值,默认值为程序的包名。在应用程序的AndroidManifest.xml文件中可以单独为每个Activity指定别的taskAffinity值(在activity标签中指定属性android:taskAffinity即可),但即使各Activity有不同的taskAffinity值,也不意味着它们就一定属于不同的Task,打个比方,一年级1班组织了一个户外植树活动,这个活动称之为“1班的植树Task”,1班的所有同学的taskAffinity值都称为“1班”,即使2班的所有同学的taskAffinity值是“2班”,但只要2班某个同学也参加这次植树活动,那么这个同学同样也属于“1班的植树Task”,并要遵守1班的任务安排。当然整个1年级也可以组织属于整个年级的植树活动“年级的植树task”,而1班和2班的同学都可以重新指定它们的taskAffinity值为“一年级”,这样两个班的同学都参加了同一次活动并同受年级管理。另外1班还可以把全班同学分为几个不同的组,每个组组织每个组自己单独的活动。在这个例子中,1班和2班代表不同的两个应用程序,从中可推出,两个不同应用程序中的Activity可以通过某种方式让它们运行于同一个Back stack(即同一个task)中;一个应用程序中的部分Activity可以安排到别的应用程序的Back stack中;一个应用程序中的各Activity可以分别安排到不同的Back stack中。

       有了以上关于TaskBack stacktaskAffinity的讨论,下面再来讲解下关于Activity启动模式等方面的内容。

每个应用程序通常都有一个主入口点,即在AndroidManifest.xml文件中为Activity指定了:

         <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

         </intent-filter>

那么在手机主菜单中就列出了这个程序的图标,当点击程序图标时,标识为主入口点的Activity就被启动了。我们知道启动Activity一般用startActivity(Intent intent)函数,很显然在Launcher的主菜单中执行了这个函数,但还有一点要注意的是,在调用这个函数时,其参数中还设置了一个标志值Intent.FLAG_ACTIVITY_NEW_TASK,这个值的意思是如果将要被启动的Activity(应用程序的Activity)与启动它的ActivityLauncher界面)的taskAffinity值不同时,那么就新建一个task及相应的Back stack,来维护新启动程序的各个Activity;而如果taskAffinity值相同时,则新启动的Activity直接放在启动它的ActivityLauncher界面)的Back stack中。由于默认情况下每个应用程序中所有ActivitytaskAffinity值都是程序的包名,而各程序都有不同的包名,所以在Launcher中启动某个程序时这个程序就会新建一个task及对应的Back stack来维护自己的Activity。接下来讲讲Activity的启动模式(launchMode)。

        在程序的AndroidManifest.xml文件的每个activity标签中可以加入android:launchMode属性,其值有四种:standardsingleTopsingleTasksingleInstance。不加属性时默认就是standard,下面讲讲其它3个值的作用:

singleTopBack stack的栈底至栈顶依次为Activity 1Activity 2时,即现在处于Activity 2界面,如果此时调用startActivity()函数再次启动一个Activity 2时,在standard模式下,我们知道之后栈底至栈顶依次为Activity 1Activity 2Activity2,而在singleTop模式下,则依次为Activity 1Activity 2。从singleTop的字面意思看,它就是单个顶端的意思,即如果一个Activity已经处于栈顶了,就不会再新建同一个Activity,此时会依次调用Activity 2的生命周期函数onPause()->onNewIntent->onResume()。但如果不是处于栈顶,例如这里启动的是Activity 1,即使是singleTop模式,那栈中内容也会变为Activity 1Activity 2Activity 1

singleTask从字面意思可看出,它代表被标志为singleTaskActivity只会在一个task即一个Back stack栈中。例如有两个应用程序,每个程序启动了两个界面,如下图所示:

                                                

                                     应用程序1                                         应用程序2

如上图,假设此时应用程序2在后台运行,并且其Activity Y位于它对应的Back stack的栈顶,而应用程序1正在运行并且当前显示界面为Activity 2,此时在Activity 2中调用startActivity()函数来启动Activity Y,如果Activity Y处于standardsingleTop模式,那么应用程序1的栈顶会加入一个新建的Activity Y,而应用程序2中的栈内容保持不变。而如果Activity Y处于singleTask,则应用程序1和应用程序2的栈都保持不变,直接跳转至应用程序2的栈顶并显示Activity YActivity Y会依次执行onNewIntent->onRestart->onStart->onResume生命周期函数。之后按下返回键就退到Activity X界面,继续按返回键,应用程序2的栈就为空了,这时它判断到它刚刚是从应用程序1跳转过来的,所有它不是回到Launcher主界面而是回到应用程序1的栈顶即Activity 2,然后继续按返回键就退到Activity 1,再按返回键应用程序1的栈也为空了,应用程序1知道它之前是直接从Launcher界面跳转过来的,所有它就退出到Launcher界面了,即每个task会记住上一个跳转到它的task是哪个,并在此task结束时跳到上一个task中。刚刚讨论的是Activity Y处于singleTask模式时的情况,当Activity X处于singleTask模式,并且在Activity 2中启动Activity X,根据singleTask的定义,这个时候不会新建Activity X的实例,而是直接跳转到应用程序2的栈中的Activity X,但是Activity Y在栈顶,要显示Activity X,就必须将Activity Y弹出栈并销毁,所以这个情况要注意,只要覆盖在Activity X上的Activity都会被Destroyed,如果是应用程序1Activity 2中启动Activity 1,而且Activity 1singleTask模式,则应用程序1的栈中Activity 2会被销毁,栈中只会保留最开始栈底的那个Activity 1。总结一下,singleTask模式的Activity,在整个系统中只会有一个实例,并且它被创建后,就始终位于某个固定的task中,至于处于哪个task,取决于第一次创建并启动它的另外那个Activity启动它时的intent参数有没有设置标志值FLAG_ACTIVITY_NEW_TASK,如果intent参数没有设置标志值,则它会安排在启动它的那个Activitytask中,如果设置了标志值,则会处于根据它的 taskAffinity值确定的那个task中。在它被创建后,以后再启动它时都不会新创建实例了,而是直接跳转到它所处的那个task中并显示它。

singleInstancesingleTask模式类似,但它的限制条件更严格,被标志为singleInstanceActivity,当第一次被创建启动时,系统一定给它创建一个单独的task及对应的Back stack栈,之后它就永远处于这个新创建的task中知道被销毁,并且这个新的task中也保证只会有它这一个Activity,即使它启动了别的Activity,被启动的Activity也会在另外的task中,被它启动的Activity会在那个ActivitytaskAffinity值确定的task中。singleInstanceActivity被创建启动后,以后任何Activity再要启动它时,都是直接跳转到它所在的task中并显示它,而不会创建它新的实例。例如在应用程序1中,首先启动Activity 1,然后启动Activity 2,而Activity 2被标志为singleInstance,这时的栈情况就如下:

                                            

                                 Task app1                                    某个单独的task

如上图,Activity 1会被安排在根据它的taskAffinity值即程序包名确认的task中,而Activity 2位于某个新创建的独一无二的task中。这时如果接着在Activity 2中再启动Activity 1,根据singleInstance的定义,新启动的Activity 1是不会被安排在Activity 2task中的,而是安排在根据Activity 1taskAffinity值确认的那个task中,即之前已经存在的Task app1(这个task名字只是我这里为了讲解而取的一个代号,系统中是用一个int值的ID来标记每个task),这时的栈情况如变为如下图所示:

                                       

                                   Task app1                                  某个单独的task

可以发现Task app1中多了一个Activity 1,而Activity 2的栈保持不变。现在显示的是Task app1中栈顶Activity 1,这时如果按返回键会出现什么情况呢?要注意的是,返回键针对的是当前显示的Activity所处的task,只有当前的task清空后,再按返回键才会返回到之前的那个task,所以这时Task app1中只剩下一个Activity 1了,如果继续按返回键,Task app1就被清空,由于它刚刚是由Activity 2的那个task跳转过来的,所以接下来就返回到Activity 2。根据某个task清空后会返回到上个跳转到它的task这个解释,要注意一个情况,还是按上图所示的状态来解释,即当前Task app1中有两个Activity 1并且手机显示的是其栈顶的Activity 1,如果之后不是按返回键而是按下Home键回到Launcher主界面,接着在Launcher中再次启动应用程序1,这时Task app1恢复,并且显示栈顶的Activity 1,接着按返回键,Task app1中只剩下一个Activity 1,再按返回键,Task app1被清空,但由于刚刚Task app1是从Launcher的主界面跳转过来的,即它是从Launcher所处的那个task跳转过来的,所以这时就退出到Launcher的界面了,而Activity 2一直在后台,无法被Destroy,如果要将Activity 2退出,必须先通过某个方式跳转到它的task并显示它,然后按返回键将它退出,例如重新进入应用程序1,通过Activity 1上的按钮或某个方式启动到Activity 2,这时再按返回键,Activity 2才可以退出了。

        上文中提到了启动Activity时的参数intent的标志值FLAG_ACTIVITY_NEW_TASK,另外还有其它两个常用的标志值FLAG_ACTIVITY_SINGLE_TOPFLAG_ACTIVITY_CLEAR_TOP。下面来讲解这三个标志值的作用。

FLAG_ACTIVITY_NEW_TASK这个标志值在上文中已经有了比较详细的介绍,它的作用是在启动一个新Activity时确认这个新Activity将被安排在哪个task中,如果加了这个参数,被启动的新的Activity就被安排在它的taskAffinity值所确定的task中,如果没加这个参数,则会安排在启动它的那个Activitytask中。这里假设两个Activity都没有加特殊的启动模式,即启动模式都为standard,如果加了其他启动模式,那就要根据上文中讲的各个启动模式的规则来。很多资料甚至Android官网的资料中都说这个标志值跟启动模式里的singleTask的作用相同,这个说法是不正确的,它们两者完全是不同的概念,各有各的作用。在Launcher中启动各应用程序时都加了这个标志值。

FLAG_ACTIVITY_SINGLE_TOP这个标志值与上文中讲的启动模式singleTop的作用一样。

FLAG_ACTIVITY_CLEAR_TOP这个值的作用比较复杂,假设目前在Activity main界面,接着启动某个Activity,暂且把这个被启动的称为Activity flag,接着从Activity flag启动到第三个界面Activity other,假设在没有启动模式等因素影响下,即它们在同一个task中,那么栈的内容如下所示:

                          

这时接着从Activity other又启动Activity flag,并且intent参数加入FLAG_ACTIVITY_CLEAR_TOP标志值,那么从位于栈中倒数第二个ActivityActivity flag开始(包括它),上面的所有Activity都被清除掉,即栈中现在只剩下了Activity main,然后会立即创建一个新的Activity flag并显示,也就是说,如果启动某个Activity时加了这个标志值,系统会搜索这个被启动的Activity将会被放置到的task,找到其中有没有这个Activity的实例,如果有,就把那个task的栈中它上面所有的Activity都销毁掉,如果这个Activity的启动模式是standard,连它自身也会被销毁掉并立即创建一个新的实例,如果启动模式是singleTopsingleTask,那才不会销毁它自身,而是只销毁它上面的所有Activity然后直接显示它。注意这里重点标记的两个字“将要”,是指根据启动模式、这个被启动的ActivitytaskAffinity值、启动它时有没有加标志值FLAG_ACTIVITY_NEW_TASK这三个因素来确定,这个Activity将会安排到哪个task中,假设根据这三个因素它即将被安排在某个叫做dest(为讲解方便随便取的一个名字)的task中,那么系统就会到task dest的栈中寻找,有没有这个Activity的实例存在,如果不存在它的实例,那么这个标志值没什么作用,跟不加的效果一样;如果已有实例存在,那就把task dest的栈中它上面的所有Activity都销毁,并且要根据它的启动模式是否为standard来确认是否连它自己也要销毁。由于FLAG_ACTIVITY_NEW_TASK这个标志值可能会影响将要被启动的Activity会被放置在哪个task中,所以FLAG_ACTIVITY_CLEAR_TOP标志值有时会与FLAG_ACTIVITY_NEW_TASK同时使用,以达到某个特殊的效果,在代码中同时使用的它们方式为:intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK)

         AndroidManifest.xml文件中除了给Activityandroid:taskAffinity属性之外,还有四个其它的跟taskAffinitytask相关的属性值:allowTaskReparentingalwaysRetainTaskStateclearTaskOnLaunchfinishOnTaskLaunch

allowTaskReparenting从字面意思上看它是指允许这个被标记的Activity重新安排在别的task中。举个例子,应用程序1中有Activity 1Activity 2两个界面,应用程序2中有Activity A一个界面。目前应用程序2没有启动即还没有创建task,并且Activity A在应用程序2中将属性allowTaskReparenting置为true;而应用程序1中启动了Activity 1并接着启动Activity A,然后又启动Activity 2;,此时应用程序1的栈内容如下:

                        

                                 应用程序1task

这时如果按下Home键回到Launcher主界面,由于Activity AallowTaskReparenting值为true,并且它是属于应用程序2的,所以这时它就从应用程序1task中被移除到属于它自己的task中,即应用程序2task,由于应用程序2目前还没有创建task,它就需要先创建一个task,由于在Launcher主界面中启动应用程序的标记为android.intent.action.MAIN即主入口点的Activity时才会创建task,所以系统会通过先创建一个虚拟的Activity A(只是做了标记,Activity A并没有创建)来启动应用程序2task,然后把应用程序1中移除的Activity A放到这个新task中,之后两个应用程序的栈内容就如下:

                   

             应用程序1task                     应用程序2task

在应用程序2task中,红字黑底的那个代表虚拟的目前还没有创建的一个Activity A实例。之后如果从Launcher中启动应用程序2,首先显示的是从应用程序1task中移除过来的Activity A,然后按下返回键时,检测到栈底还有一个虚拟的Activity A,所以会将其由虚拟变为现实的,即创建一个Activity A的实例,这时栈中就剩下这个之前为虚拟而现在为真实存在的Activity了,继续按返回键,应用程序2task结束。

alwaysRetainTaskState假设一个应用程序的task中有了一些Activity,当按Home键返回到Launcher主界面后,如果之后很长一段时间都不重新进入到这个应用程序,那么系统很有可能会将task中除栈底的那个Activity之外的所有Activity丢弃掉。但如果栈底的那个ActivityalwaysRetainTaskState属性值设为true,则即使过了很长一段时间,系统通常也不会丢弃task中的任何Activity

clearTaskOnLaunch它与上一个属性alwaysRetainTaskState作用相反,即不管经过多长时间,只要在栈底的ActivityclearTaskOnLaunch属性为true,那么重新进入程序时所有除栈底外其他的Activity一定会被丢弃掉。这个属性的优先级要高于alwaysRetainTaskState,即在栈底的Activity如果同时将这两个属性设为true,那alwaysRetainTaskState的设置无效。

finishOnTaskLaunch 与clearTaskOnLaunch类似,不过它只针对除栈底外的其它单个的Activity,如果某个Activity的finishOnTaskLaunch属性值为true,则重新进入程序时,这个Activity必定被丢弃掉了。它的优先级也高于alwaysRetainTaskState属性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值