前言
总所周知,Activity有四种启动方式,但很少谈到它还有多种flag帮帮助我们来设定不同的Activity启动方式,这里我们就来踩一踩starActivity的那些坑
任务栈
要了解Activity的启动方式那些事,首先要先了解下任务栈
- 每次打开应用都会创建一个任务栈,用于存储当前程序的,所有的activity属于一个任务栈。
- 一个任务栈包含了一个activity的集合, 只有在栈顶的activity才可以跟用户进行交互。
- 任务栈可以移动到后台, 并且保留了每一个activity的状态。
- 当把所有的任务栈中所有的activity清除出栈时,任务栈会被销毁,程序退出。
任务栈的缺陷
- 每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用,户体验差, 需要点击多次返回才可以把程序退出了。
- 每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余, 重复数据太多, 会导致内存溢出的问题(OOM)。
启动模式
启动模式就是为了解决任务栈的缺点而提出来的。
启动模式(launchMode)在多个Activity跳转的过程中扮演着重要的角色,它可以决定是否生成新的Activity实例,是否重用已存在的Activity实例,是否和其他Activity实例公用一个task里。这里简单介绍一下task的概念,task是一个具有栈结构的对象,一个task可以管理多个Activity,启动一个应用,也就创建一个与之对应的task。
- standard(默认的启动模式,每次开启都会创建一个新的Activity实例)
- singleTop(栈顶复用模式)
- singleTask(栈内复用模式)
- singleInstance(单实例模式)
正文
前三种启动模式相信大家已经很熟悉了,这篇博客相信很多人也看过,但还有很多情况没有提及到,这里就做一下补充
Q:如何如何查看有多少个任务栈和任务栈里有多少个Activity及分别是那些Activity?
使用adb,里面有一条指令可以查看手机中的任务栈
- 打开SDK Manager,查看自己的SDK安装目录
- SDK安装目录下有一个platform-tools目录,里面就是你adb的安装目录
- 打开cmd,cd 到adb目录下,使用adb shell dumpsys activity activities查看任务栈
可以看到在栈ID为80的栈下有4个Activity,栈顶是e985b73这个Activity
singleInstance
Q:singleInstance是开启一个新的任务栈,那么把Activity设定为singleInstance重复启动会是什么情况?
Activity A 是standrad启动模式,Activity B是singleInstance启动模式,A ->B之后,B重复启动B会发生什么?
看来singleInstance启动之后在再启动自己不会创建新的实例
那么继续,再加一个Activity C,standrad启动模式,A->B->C->B 会发生什么事?
gif演示的比较快,不方便看,可以看下这个图
那回退呢?是怎么样的
上面的是A->B-C->B->C启动,如果是A->B->C->B启动的话,回退是这个样子的
因为通过singleInstance启动的Activity会单独的压入一个新的栈中,有且只有一个,又居于栈内复用的特性,所以不会重复创建新的实例
Q:使用singleInstance模式的Activity通过startActivityForResult会发生什么事
Activity A 是standard,Activity B是singleInstance启动模式,从B返回A会弹出一个吐司显示valus
可以看到,singleInstance失效了,它还是在栈77里面,并没有开启新的栈
我试验了多次,发现了如下规则,具体原因是Android底层做的优化,但为什么这样做是不知道的
Android 5.0 之前:
A通过startActivityForResult启动B会在一个新的任务栈中,但onActivityResult是拿不到返回的intent的
Android 5.0之后:
A通过startActivityForResult启动B会在同一个任务栈中,在通过startActivity启动C(C是standard),C会在新的任务栈中,如果还是startActivityForResult,则依旧在同一个任务栈中,所以onActivityResult方法也会正确触发。
这一规则同样适用于singleTask设置不同的taskAffinity来新的栈
Q:A、B在不同的任务栈,通过A启动B后,在从B回到A,如何获取到B的数据?
我们知道正常情况下是可以通过startActivityForResult获取到传回的数据,但是在不同任务栈中会失效,startActivityForResult的源码也有注释
/**
* <p>Note that this method should only be used with Intent protocols
* that are defined to return a result. In other protocols (such as
* {@link Intent#ACTION_MAIN} or {@link Intent#ACTION_VIEW}), you may
* not get the result when you expect. For example, if the activity you
* are launching uses {@link Intent#FLAG_ACTIVITY_NEW_TASK}, it will not
* run in your task and thus you will immediately receive a cancel result.
*/
singleTop和singleTask复用自己的时候都会调用onNewIntent,可以在这里获取到intent来取出从B传回的值
Activity的常用Flag
- FLAG_ACTIVITY_NEW_TASK
和“SingleTask”一模一样 - FLAG_ACTIVITY_SINGLE_TOP
和“SingleTOP”一模一样 - FLAG_ACTIVITY_CLEAR_TOP
当它启动时,在同一个任务栈所以位于它上面的Activity都要出栈,SingleTask默认有此标记效果;
如果Activity是standrad,那么它连同它之上的Activity都要出栈,并创建一个新的Activity实例放入栈内。 - FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有该flag的Activity不会出现在历史Activity的列表中,同excludeFromRecents="true"