Android的SingleTask,SingleInstance和StartActivityForResult冲突问题

时间:2024-03-15 12:20:32

Android的SingleTask和StartActivityForResult冲突问题

首先回顾下Android的四种启动模式:

  • standard( 标准模式 ):每次启动一个Activity都会重新创建一个新的实例,不管这个实例是否存在,并且它会进入启动者所在的栈中。
  • singleTop(栈顶复用模式): 如果新的Activity已经位于所在任务栈的顶部,则此Activity不会被重新创建 ,若已存在但不在栈顶,则仍然会重新创建;
  • singleTask(栈内复用模式): 只要Activity在一个栈已经存在,那么多次启动此Activity都不会重新创建实例。
  • singleInstance(单实例模式): 这是一种加强的singleTask模式,当该Activity启动后,系统会为该Acitivty创建一个新的栈,然后改Acitivty会独立地在这个栈内,但由于栈内复用特性,后续的请求均不会创建新的栈,除非这个独立的栈被系统销毁。

standard和singleTop一般比较熟悉也很简单。这次主要是探讨下singleTask和singleInstance的坑。

最近遇到一个bug。项目是先进到首页的,然后在首页中有异常抛出就会通过StartActivityForResult跳到到登录界面,由于框架的结构,会出现多次调整登录界面的情况,但无所谓嘛,把登录界面的启动模式设置为SingleTask就好了。

但是问题出现了,在Activity启动集合工具类里面发现,登录界面被多次创建,那究竟是怎么回事呐?
怀疑是StartActivityForResult搞的鬼。点进去看了一下源码:


Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


这大概意思就是如果你要跳转的Activity是SIngleTask启动的,那么会无效并且立刻在onActivityResult中收到RESULT_CANCLE的回调。
好了,说那么多没用,我们来写个栗子来验证一下:

就两个界面:
A界面:可以通过StartActivity和StartActivityForResult两种方式进行跳转到B界面:

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


B界面:可以通过StartActivity和setResult两种方式进行跳转A界面:

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


清单文件配置:
Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


界面A的代码:

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


界面B的代码:

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


好了下面,以Andorid4.4为测试机子试验一下:

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


然后再以Android 6.0的机子试验一下:

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


然后把B界面的启动模式改为singleInstance启动模式再试验一下:

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


以Andorid4.4为测试机子试验一下:

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


然后再以Android 6.0的机子试验一下:

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题


是不是好神奇,在4.4系统B界面的setResult方法是无效的,直接在A界面的onActivityForResult方法回调RESULT_CANCELED, 而 在6.0手机上则是直接重新创建B界面。
查看 相关的文章,Google的官方文档有提到相关的问题

Android的SingleTask,SingleInstance和StartActivityForResult冲突问题

如上图,当在Activity2中调用BackGroundTask中已经启动过的ActivityY(ActivityY的启动模式为SingleTask),则BackgroundTask内占据屏幕并且该Task下所有栈都会保留当前的栈位置及顺序push进Back Stack形成新的结构,顺序由上至下为ActivityY –> ActivityX –> Activity2–>Activity1。
在ActivityY界面按返回键,则ActivityY出栈,ActivityX占据屏幕。!注意,ActivityY是由Activity2启动的,但是在ActivityY按返回键并没有直接回到Activity2,而是回到ActivityX!所以在ActivityY执行setResult(),Activity2也无法接收。
所以回到前面的例子,A通过StartForResult启动模式为SingleTask且已存在的B,而B的setResult的结果是只能会掉到onActivityResult给出RESULT_CANCEL也就可以理解了。