应用被其他app打开时,不同启动模式下activity任务栈的变化

3,343 阅读4分钟

问题

问题描述:我们的app被其他app启动进入MainActivity,然后MainActivity连续调用两次startActivity分别启动IndexActivity和CommonActivity,先启动的IndexActivity,然后启动的CommonActivity,然后神奇的是屏幕上显示的是IndexActivity,按返回关闭IndexActivity后才显示CommonActivity。

这个问题是同事在做被第三方app打开这个功能时发现的,他自己写的demo测试都是正常的,移到我们项目上来就不行了,他对比了下发现是IndexActivity的launchMode设置的singleTask,去掉就是正常的了,但是这个singleTask又是其他功能需要的不能去(这个功能是我加的),所以最后反馈到我这了。

当时觉得是很神奇但是又不知道为什么,自己跑了下确实像同事说的去掉singleTask就好了,考虑到启动模式会影响activity的任务栈,想先看一下加了singleTask时的任务栈和去掉后的任务栈有什么区别。然后就百度了下,确实可以查看当前的任务栈,adb命令

adb shell dumpsys activity activities

具体打印出来的信息很多,这里就不放了,说一下从里面发现了什么。

结果

假定第三方app为App1,我们的app为App2,App1的TestActivity启动App2的MainActivity,然后MainActivity连续调用两次startActivity分别启动IndexActivity和CommonActivity,先启动的IndexActivity,然后启动的CommonActivity,其中IndexActivity的LaunchMode设置singleTask,。 预期的activity栈如下:

预期的activity栈
但实际上现在是存在两个activity栈了:

实际上activity栈
此时MainActivity和CommonActivity和App1的TestActivity在同一个任务栈,IndexActivity因为singleTask是会新开一个任务栈,(如果IndexActivity没有指定taskAffinity,那么这个任务栈名字就是App2包名,如果指定了taskAffinity就会用taskAffinity指定的名字),本来CommonActivity是在IndexActivity之后启动的,但是因为IndexActivity所在任务栈是新创建的,这样就会使IndexActivity在前台而显示出来,显示在最上面,此时按返回键会先finish IndexActivity然后显示出来CommonActivity。

以普通模式启动的activity会进入启动它的activity的任务栈,所以当MainActivity启动了IndexActivity和CommonActivity,因为MainActivity是普通模式启动的,会被加到App1的TestActivity所在的任务栈,CommonActivity也是普通模式启动,会被加到MainActivity所在任务栈也就是TestActivity的任务栈,设置他们的taskAffinity也没用(只有singleTask,singleInstance这样会创建新任务栈的才有用),这样MainActivity和CommonActivity都在TestActivity所在的任务栈中。

解决

要想达到前台显示的是CommonActivity,就必须让MainActivity、IndexActivity、CommonActivity在一个任务栈,所以可以把MainActivity的启动模式也设置为singleTask,这样MainActivity被加到了一个新任务栈(如果不指定taskAffinity,那么这个任务栈名字就是App2包名),MainActivity再启动了IndexActivity和CommonActivity,并且IndexActivity不指定taskAffinity(默认使用包名作为任务栈名),这样和MainActivity任务栈名字一样,加到MainActivity所在任务栈,CommonActivity是普通模式启动,也会加入到启动者MainActivity所在任务栈,这样三者就都在一个任务栈中了,因为CommonActivity是后启动的,它会显示在前台,所以问题就是这样。如下:

问题解决

附加

还有singleInstance启动的,同样指定了taskAffinity就用指定的作为任务栈名,没有指定就用包名,会创建一个新任务栈A(如果有同名的任务栈表示已经创建过了就重用,不会创建新任务栈)并且里面只有一个Activity,哪怕后面他又启动了一个singleTask的Activity并且任务栈名字相同也不会影响任务栈A,singleInstance启动的Activity所在的任务栈如果没有就创建,如果有就重用,并且里面只有一个Activity。

思考

前面提到的taskAffinity,回到最开始的问题,假如我把MainActivity的taskAffinity指定为App2包名,这样虽然App2的IndexActivity的启动模式是singleTask,但是他们任务栈名字一样应该会在同一个任务栈吧。其实是不行的,taskAffinity可以和allowTaskReparenting配合使用,当allowTaskReparenting设为true时,同时指定taskAffinity等于App2的包名,还是像开始的那样操作,MainActivity和CommonActivity还是会在App1的TestActivity的任务栈中,当按了Home键回到桌面后在打开App2,此时MainActivity才会在以App2包名命名的任务栈中。