1.1 Activity的LaunchMode
activity启动模式基本知识不在描述。本文主要是在使用过的前提下进行了进一步的认知。首先任务栈大家都知道,先进后出、后进先出。如果任务栈没有activity了,那就会被系统所回收。Activity启动模式有四种:standard、singleTop、singleTask、singleInstance。
1.1.1 standard
标准模式、系统默认模式,每次启动均会创建实例压入栈中。
这里有一个点:编写类似下面代码启动activity会报错:
MyApplication.getInstance().startActivity(
new Intent(MainActivity.this, AAy.class));
报错:
android.util.AndroidRuntimeException:
Calling startActivity() from outside of an Activity context requires
the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
这是因为正常standard启动模式的activity默认会进入启动它的Activity所属的任务栈中(singleInstance除外),但是非activity的Context没有任务栈(任务栈是针对activity),所以出了上诉的错误和建议,加上以下设定即可:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
此时实际上以singleTask模式启动activity。
1.1.2 singleTop
这种模式简单来讲就是如果被启动activity位于栈定,那么就不会被重新创建,同时调用onNewIntent方法。
这个地方有一点小逻辑,比如两个Activity,A和B,A为singleTop模式,A启动了B,然后在B中这样子:
finish();
startActivity(new Intent(B.this, A.class));
你会发现这个时候复用了A,而且调用了A的onNewIntent方法。这种效果有点类似于startActivityForResult方法。再者如果这样子调用的话:
startActivity(new Intent(B.this, A.class));
finish();
是会创建一个新的A。其实也就是说finish方法有一个作用是把当前activity移除栈
如果正常B直接finish的话,A是会调用onRestart方法。
还有一种情况是:
有两个activity,A和B,A启动模式为singleTop,B启动模式为singleTask且设定taskAffinity="a.a",那么先启动A,然后A中启动B,B再启动A。这时候会发现虽然A和B归属两个不同的任务栈,但是在最后B再启动A时系统默认还是会遍历任务栈是否有需要的任务栈在存在,并且A是否位于顶部,有的话就会调用A的onNewIntent。但是如果A的启动模式为默认的,那么先启动A,然后A中启动B,B再启动A,那么就是第一个A是在默认的栈中,B和第二次启动A在栈a.a中。
1.1.3 singleTask
栈内复用模式,如果singleTask模式启动的activity存在一个栈中,则会被复用,并调用其onNewIntent方法。判断的的方式是如果要启动activity,首先会查找所需的栈是否存在,不存在则新建栈并放入activity,如果存在则会压入栈中。并且自带clearTop效果。
- 如果存在任务栈S1,所需任务栈为S2,那么会创建S2并放入activity。
- 如果存在任务栈S1,所需任务栈为S1,那么新建activity压入栈中,无论此时栈中是否有activity。
- 如果存在任务栈S1,S1内有被启动的activity,那么会复用S1栈的activity并调用onNewIntent方法。此时会移除此activity上面的activity,移到栈顶。
这里有个点:如果MAIN的activity是singleTask模式的话会出现从桌面图标每次进入都会是mainActivity,而不是缩到后台的那个activity的情况。而且此时的mainActivity的并不是重新创建。不过主页直接是MAIN Activity的情况也不太多。
1.1.4 singleInstance
加强版的singleTask。会单独创建一个任务栈,并且只放被启动的activity。这里有一种情况,如果A启动B,B启动A,B启动模式为singleInstance。那么这时候会发现AA是在一个栈内,B一个栈。这是因为singleInstance时只允许存在一个B,而且从B启动的activity也不会和B在一个栈内。
下面说一种多任务栈的情况,如图:
两个任务栈,S1包含activity1和activity2,S2包含activity Y和activity X,且activity Y启动模式为singleTask。此时activity2启动activity Y,那么S2任务栈移到前台。此时按back按键,则activity Y移除栈,再按back按键,则移除activity X,此时S2任务栈没有activity了,则被回收。S1任务栈移到前台。
1.2 指定activity启动模式
两种方式:
和
Intent intent = new Intent(MainActivity.this, AAy.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
区别:
- 1、优先级为第二种最高
- 2、使用范围不同,第一种无法直接为activity设定FLAG_ACTIVITY_CLEAR_TOP,第二种无法为activity设定singleInstance。
1.3 任务栈
默认所有activity所在的栈名为应用的包名。自定义的话可以在manifest中设定,如;
android:taskAffinity="a.a"
比如在application中设定,那么任务栈的名称就变成了".a.a"
Stack #1:
Task id #3474
TaskRecord{596be1d #3474 A=a.a U=0 sz=2}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.ccddy.test/.MainActivity }
Hist #1: ActivityRecord{11d80d35 u0 com.ccddy.test/.AAy t3474}
Intent { cmp=com.ccddy.test/.AAy }
ProcessRecord{3689f892 18292:com.ccddy.test/u0a196}
Hist #0: ActivityRecord{210ce749 u0 com.ccddy.test/.MainActivity t3474}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.ccddy.test/.MainActivity }
ProcessRecord{3689f892 18292:com.ccddy.test/u0a196}
查看任务栈方法:在AS中点击Terminal并输入命令:
adb shell dumpsys activity
找到ACTIVITY MANAGER ACTIVITIES,即可
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):
Stack #1:
Task id #3474
但是如果在activity的标签中设定taskAffinity,并且activity启动模式例如为standard是没有意义的,而且栈的名称还是默认包名。1、但是如果taskAffinity和singleTask配对使用时,可以把singleTask放在指定的任务栈中(这种情况下和singleInstance是不一样的,如果A是这种模式,那么由A调起的activity是和A在一个任务栈内的)。2、taskAffinity和allowTaskReparenting结合使用:比如当一个应用A启动了应用B的某个activity后,如果这个activity的allowTaskReparenting为true时,然后按home按键回到桌面,点击B的桌面图标,这时候不是启动B的主activity,会启动被A启动的那个activity,也就是说把activity从A的任务栈中拉入了B中的任务栈中了。
1.4 Activity的Flags
标记为比较多,一般不会使用,而且有些是系统内部设定使用的。主要说明一下四种(不止四种哦):
FLAG_ACTIVITY_NEW_TASK
作用和singleTask一样
FLAG_ACTIVITY_SINGLE_TOP
作用和singleTop一样
FLAG_ACTIVITY_CLEAR_TOP
有点类似singleTask的clearTop效果。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标记的activity不会出现在最近运行任务的列表中(比如小米按菜单键那个运行中的APP列表)。和xml中设定android:excludeFromRecents="true"。
1.5 清理返回栈
如果用户长时间离开任务,则系统会清除所有 Activity 的任务,根任务除外。 当用户再次返回到任务时,仅恢复根 Activity。系统这样做的原因是,经过很长一段时间后,用户可能已经放弃之前执行的操作,返回到任务是要开始执行新的操作。
您可以使用下列几个 Activity 属性修改此行为:
alwaysRetainTaskState
如果在任务的根 Activity 中将此属性设置为 "true",则不会发生刚才所述的默认行为。即使在很长一段时间后,任务仍将所有 Activity 保留在其堆栈中。clearTaskOnLaunch
如果在任务的根 Activity 中将此属性设置为 "true",则每当用户离开任务然后返回时,系统都会将堆栈清除到只剩下根 Activity。 换而言之,它与alwaysRetainTaskState正好相反。 即使只离开任务片刻时间,用户也始终会返回到任务的初始状态。finishOnTaskLaunch
此属性类似于 clearTaskOnLaunch但它对单个 Activity 起作用,而非整个任务。 此外,它还有可能会导致任何 Activity 停止,包括根 Activity。 设置为 "true"时,Activity 仍是任务的一部分,但是仅限于当前会话。如果用户离开然后返回任务,则任务将不复存在。