activity的启动模式和任务栈

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/y1962475006/article/details/52403836

大家应该都知道activity的四种启动模式,但是activity应该在哪个栈启动?不同的栈有什么区别?里面会有什么坑?下面会说说我所了解到的。
一、
先说一下这四种模式:
1、
standard:标准模式。每次都在任务栈里创建新的实例,而不管这个栈里之前有没有。
singleTop:栈顶复用。如果要启动已经在栈顶的singleTop 的activity,就直接复用,而不是重新实例一个。
singleTask:栈内复用。如果要启动已经在栈内的singleInstance的activity,会把它上面的activity都出栈,并复用它。
singleInstance:每次启动singleInstance的activity,会新建一个任务栈。
二、
再说任务栈:
每次back按键就是一次出栈,知道任务栈被清空,回到桌面,任务栈被回收。任务栈在android中并不是唯一的,分为前台栈和后台栈。怎么区分是前台栈还是后台栈呢?简单点说,就是现在你能看到的activity所在的任务栈,其他都是后台栈,也就是说这个是相对的。后台栈的activity启动,显示出来,那么后台栈就成了前台栈了。这里要注意,如果是由前台栈启动后台栈中的一个actiity,那么整个后台栈都会放在前台栈前作为新的前台栈。android会对不同任务栈的activity跳转右不同的动画(同一个任务栈内的跳转和不同任务栈的跳转不同)
怎么看我的应用现在任务栈的情况呢?
在terminal中敲命令:adb shell dumpsys activity
直接找Running activities:

Stack#20: Running activities (most recent first):
TaskRecord{a24653b #814 A=android.crm.paic.com.yueshaojun U=0 sz=1}
Run #12: ActivityRecord{3faa76 u0 android.crm.paic.com.yueshaojun/.activity.FourActivity t814}
TaskRecord{a563911 #813 A=com.yue U=0 sz=1}
Run #11: ActivityRecord{e72481c u0 android.crm.paic.com.yueshaojun/.activity.ThreeActivity t813}
TaskRecord{bfbcd8f #812 A=android.crm.paic.com.yueshaojun U=0 sz=2}
Run #10: ActivityRecord{bd2297 u0 android.crm.paic.com.yueshaojun/.activity.TwoActivity t812}
Run #9: ActivityRecord{e4eea3c u0 android.crm.paic.com.yueshaojun/.activity.OneActivity t812}

从上到下是stack的top到bottom。每个TaskRecord{XXX}都是一个任务栈(所有的任务栈都是在stack中的,整体还是个stack,可以自己体会下任务栈和stack的不同)在TaskRecord中 #后面的数字是唯一标识,A=“XXX”是栈的名字,默认的是应用包名,也可以自己定义(后面说怎么自己设置)sz(size)是任务栈的大小。一般情况下不同A(名字)就是不同的任务栈,但是也有可能相同的名字是不同的任务栈,所以#才是区分的唯一标识。
三、
既然有这么多任务栈,那么每次启动一个activity的时候到底要把这个activity放在哪个栈里呢?
这就又和我们之前说的启动模式有关了。
standard:谁启动了standard模式的activity,那么这个activity就运行在谁所在的栈中。例如,A启动了B ,B是standard,那么B会运行在A所在的栈中。
singleTop:和standard一样。
singleTask:启动了一个singleTask的activity,那么这个activity运行在默认任务栈中(以包名为名)。但是如果singleTask指定了taskAffinity属性,像这样:
android:launchMode=”singleTask”
android:taskAffinity=”com.shao”
那么,activity会运行在一个名为com.shao的任务栈中,如果这个栈不存在,会新建这样一个栈。
但是如果是通过startActivityForResult去启动一个singleTask的activity,在5.0以上,会singTask会失效,等同于standard模式,也就是说,A通过startActivityForResult启动一个singleTask的B,B会运行在A所在的任务栈(和standard模式一样)。
singleInstance:启动了一个singleInstance的activity,如果没有指定taskAffinity,这个activity会运行在一个和默认任务栈包名一样但是#后面数字不同的新的任务栈(#其实就是id啦)。如果指定了包名,且这个包名在stack中没有相同的,那就运行在这个包名命名的新任务栈中;如果有相同的,就是#后数字(id)不一样。(说白了就是一定要在一个新的任务栈里)另外在5.0以上通过startActivityForResult启动singleInstance 模式的activity,也会清除singleInstance属性。
四、可能的坑
1、如果用Application或者service的context去启动一个standard的activity,那么会因为找不到任务栈报错,需要指定FLAG_ACTIVITY_NEW_TASK(相当于指定了singleTask模式)
2、根据我的测试android5.0以上,三中startActivityForResult才会清除singleTask属性,但是5.0以下(测试了4.4.4)是不会清除的,这样会导致,A启动了B,在onActivityResult中获得的resultCode是0(cancel),而且是启动B的同时就收到0(因为不在一个栈中)。具体的例子:
A singleTask 指定taskAffinity=“com.xxx1” B singleTask 指定taskAffinity= “com.xxx2”在5.0以上,B会在A的任务栈中运行,也就是名为“com.xxx1”的栈,而在5.0以下,会在新的名为“com.xxx2”的栈中运行。
3、
singleTask模式启动(如果目标任务栈中有这个activity)会先clearTop,栈顶的其它activity都会出栈。这点要注意。


总结:由此可见,activity在栈中的位置可能跟据不同的启动模式有不同的组合,只有搞清楚它们的规则,才能避免不必要的麻烦。


以上是我自己通过实例发现的,如有不正确的地方,还请多多指教

猜你喜欢

转载自blog.csdn.net/y1962475006/article/details/52403836