【笔记】【Android】Activity启动模式
基本概念
1. Task
一般情况下一个App只有一个 Task,名字为该App的包名。在手机的最近开启的App列表中,每一个 Task 就占据一个位置。即切换应用的基本调度单位为 Task。
一个 Task 下包含了一个 Activity栈。
2. Activity栈
表示一系列的 Activity,遵循先进后出,后进先出的原则。
3. 图像描述

启动模式
AppA
com.bin.appa
AppB
com.bin.appb
各种Activity的属性
StandardActivity
启动模式:standard
是否可跨进程打开:是
指定Task:默认包名
SingleTopActivity
启动模式:singleTop
是否可跨进程打开:是
指定Task:默认包名
SingleTaskActivity
启动模式:singleTask
是否可跨进程打开:是
指定Task:默认包名
SingleInstanceActivity
启动模式:singleInstance
是否可跨进程打开:是
指定Task:默认包名
TaskSingleInstanceActivity
启动模式:singleInstance
是否可跨进程打开:否
指定Task:bin.test.task
TaskSingleTaskActivity
启动模式:singleTask
是否可跨进程打开:否
指定Task:bin.test.task
TaskSingleTopActivity
启动模式:singleTop
是否可跨进程打开:否
指定Task:bin.test.task
TaskStandardActivity
启动模式:standard
是否可跨进程打开:否
指定Task:bin.test.task
1. Standard
默认的启动方式,每次启动该模式的Activity时,都会新建一个实例并加入到当前Task的Activity栈顶。如果是在启动模式为 singleInstance 的Activity中启动,那么分两种情况:
- 当前App内存在指定的Task,则直接将该Activity放到Task。
- 当前App内不存在指定的Task,则新建一个Task再放入。
就类似于设置了 FLAG_ACTIVITY_NEW_TASK。
使用场景
一般情况下都使用该模式启动活动。
adb命令观察
动作:打开AppA,打开AppB。AppA 创建一个 StandardActivity→创建一个 StandardActivity→打开 AppB 的 StandardActivity。
预计结果为:AppA中只有一个Task,Task中的Activity栈顶的Activity为 AppB 的 StandardActivity。AppB中只有一个Task,Task中的Activity栈只有一个 MainActivity。
使用1观察:
Running activities (most recent first):
TaskRecord{4971b86 #169 A=com.bin.appa U=0 sz=4}//APP A
Run #10: ActivityRecord{285089f3 u0 com.bin.appb/.StandardActivity t169}//包名为APP B
Run #9: ActivityRecord{1fe06e75 u0 com.bin.appa/.StandardActivity t169}
Run #8: ActivityRecord{a50fe67 u0 com.bin.appa/.StandardActivity t169}
Run #7: ActivityRecord{3080b171 u0 com.bin.appa/.MainActivity t169}
TaskRecord{14396a12 #171 A=com.bin.appb U=0 sz=1}//APP B
Run #6: ActivityRecord{2d90525 u0 com.bin.appb/.MainActivity t171}
2. SingleTop
Activity栈顶模式,启动该模式Activity时,会先检测当前Task 中的Activity栈的栈顶是否为该活动。如果是,则复用该活动,会回调 onNewIntent() 这个生命周期;如果不是,则新建一个该Activity放入当前Task的Activity栈顶。
使用场景
需要先登录才能使用的App,并且不允许账号同时在线。此时可以将登录活动的启动模式设置为 singleTop。这样当账号在其他设备登录时,直接启动登录活动即可。无需担心用户使用“回退键”跳过登录。
adb命令观察
- 动作:打开AppA。创建一个
StandardActivity→创建一个SingleTopActivity→创建一个SingleTopActivity→创建一个StandardActivity→创建一个SingleTopActivity。
预计结果:AppA中只有一个Task,Task中的Activity栈由上到下为:
|SingleTopActivity|
|StandardActivity |
|SingleTopActivity|
|StandardActivity |
| MainActivity |
使用2观察:
Running activities (most recent first):
TaskRecord{13b3903 #172 A=com.bin.appa U=0 sz=5}
Run #10: ActivityRecord{4b96105 u0 com.bin.appa/.SingleTopActivity t172}
Run #9: ActivityRecord{38b93877 u0 com.bin.appa/.StandardActivity t172}
Run #8: ActivityRecord{a1de7d9 u0 com.bin.appa/.SingleTopActivity t172}
Run #7: ActivityRecord{3fe333ab u0 com.bin.appa/.StandardActivity t172}
Run #6: ActivityRecord{759b942 u0 com.bin.appa/.MainActivity t172}
- 动作:打开AppA。创建一个
StandardActivity→创建一个SingleTopActivity→使用FLAG_ACTIVITY_NEW_TASK创建一个TaskStandardActivity→创建一个SingleTopActivity
预计结果:AppA中有两个Task,分别为默认名称为包名的Task和自定义的Task。
包名Task中的Activity栈内由上到下为:
|SingleTopActivity|
|StandardActivity |
| MainActivity |
自定义Task中的Activity栈内由上到下为:
| SingleTopActivity |
|TaskStandardActivity|
Running activities (most recent first):
TaskRecord{32dad336 #180 A=bin.test.task U=0 sz=2}//自定义task
Run #10: ActivityRecord{213784e0 u0 com.bin.appa/.SingleTopActivity t180}
Run #9: ActivityRecord{2e381c57 u0 com.bin.appa/.TaskStandardActivity t180}
TaskRecord{26d733a4 #179 A=com.bin.appa U=0 sz=3}//APP A默认task
Run #8: ActivityRecord{12fdb4b9 u0 com.bin.appa/.SingleTopActivity t179}
Run #7: ActivityRecord{241a618b u0 com.bin.appa/.StandardActivity t179}
Run #6: ActivityRecord{113c59c6 u0 com.bin.appa/.MainActivity t179}
3. SingleTask
每次启动该模式的Activity时,会先寻找是否有该Activity指定的Task,默认Task为包名,可通过 taskAffiny 指定名称。如果不存在指定的Task,则新建一个Task并将该Activity放入。如果存在指定的Task,则检查其中是否有该Activity。如果有,则移除它上面的所有Activity;如果没有,则将该Activity放入栈顶。
该启动模式的Activity所处的Activity栈内,只存在一个该Activity。
使用场景
如微信小程序,需要在最近任务列表中显示小程序和微信两个卡片。并且小程序中有自己的一套Activity栈。
这个只是我的假设,不一定微信小程序就是这么实现的。
adb命令观察
- 动作:打开AppA。创建一个
StandardActivity→通过FLAG_ACTIVITY_NEW_TASK创建一个TaskStandardActivityt→创建一个TaskSingleTaskActivity→创建一个StandardActivity→通过最近任务列表返回第一个StandardActivity→创建一个TaskSingleTaskActivity
预计结果:AppA有2个Task,一个名称为包名,一个的名称为 bin.test.task。
包名Task中的Activity栈由上到下为:
| StandardActivity |
| MainActivity |
自定义Task中的Activity栈由上到下为:
|TaskSingleTaskActivity|
| TaskStandardActivity |
Running activities (most recent first):
TaskRecord{1314e94e #190 A=bin.test.task U=0 sz=2}
Run #9: ActivityRecord{1e82e908 u0 com.bin.appa/.TaskSingleTaskActivity t190}
Run #8: ActivityRecord{31e6273e u0 com.bin.appa/.TaskStandardActivity t190}
TaskRecord{3b24b37c #189 A=com.bin.appa U=0 sz=2}
Run #7: ActivityRecord{3166f6a8 u0 com.bin.appa/.StandardActivity t189}
Run #6: ActivityRecord{17c5806 u0 com.bin.appa/.MainActivity t189}
4. SingleInstance
启动该模式的Activity时,检测所有Task内是否存在该Activity。如果有,则回调 onNewIntent(),并将该Task移动到前台;如果没有,则新建一个Task并将该Activity放入栈中。
该模式的Activity独享一个Task。
使用场景
如地图类应用,需要提供给第三方App调起地图。
adb命令观察
动作:打开AppA。创建一个 StandardActivity→创建一个 SingleInstanceActivity→创建一个 StandardActivity→创建一个 SingleInstanceActivity。
预计结果:AppA中存在两个Task,两个Task的名称一致。
第一个Task中的Activity栈由上到下为:
|SingleInstanceActivity|
第二个Task中的Activity栈由上到下为:
|StandardActivity|
|StandardActivity|
| MainActivity |
Running activities (most recent first):
TaskRecord{2754349 #193 A=com.bin.appa U=0 sz=1}
Run #9: ActivityRecord{39dd2b16 u0 com.bin.appa/.SingleInstanceActivity t193}
TaskRecord{6a0996f #191 A=com.bin.appa U=0 sz=3}
Run #8: ActivityRecord{39b42723 u0 com.bin.appa/.StandardActivity t191}
Run #7: ActivityRecord{50225c0 u0 com.bin.appa/.StandardActivity t191}
Run #6: ActivityRecord{14dedf63 u0 com.bin.appa/.MainActivity t191}
总结
四个启动模式可以分成两类,第一类是 standard 和 singleTop,它们都可以存在多个实例;第二类是 singleTask 和 singleInstance,它们只会存在一个实例。
| 多实例 | 单实例 |
|---|---|
| stadard | singleTask |
| singleTop | singleInstance |
为什么有了Activity栈后还要有Task存在
主要是为了方便多个App之间互相调用时,有无缝的体验感。比如在写日记的时候,可以调起系统相机进行拍照,拍完之后还能进入相册选照片。这一系列的操作,都如同在一个日记App内完成的一样。这就是Task存在的目的。