前面两篇文章Activity启动模式与TaskAffinity属性和Intent与启动模式相关的Flag简介 ,想必大家对任务栈有了一定理解。
1.什么是任务(栈)
1.在 Android 中,每一件事可以被看作是一个 Task,一个 Task 又可以被细分成多个子步骤,每个子步骤可以被看作是一个 Activity。任务是用户在执行某项工作时与之互动的一系列 Activity 的集合。这些 Activity 按照每个 Activity 打开的顺序排列在一个返回堆栈中。堆栈中的 Activity 永远不会重新排列,只会被送入和退出,在当前 Activity 启动时被送入堆栈,在用户使用返回按钮离开时从堆栈中退出。因此,返回堆栈按照“后进先出”的对象结构运作,所以也叫任务栈。
小结:
- 每一个应用进程就是一个任务,这个任务中又包含多个Activity,如果不指明,那么一个应用会共享自己同一个任务栈,当然应用自己可以定义多个任务。
- 整个Activity的堆栈是由多个任务堆叠而成的,每个任务里面又可以管理自己的返回栈,任务里面的Activity是有顺序的。
- 任务列表是有顺序排列的,同一个任务中的activity增减是activity的入退场,而当这个任务中的activity都被出栈完后就涉及到切任务和activity退场两个逻辑。
- 因为堆栈中的Activity永远不会重新排列,所以如果要起的Activity是一个singleTask的,那么在这个Task中这个Activity之上还有其他Activity的都会被finish掉,以便能够让它被显示到前台。
直观感受:
比如当用户使用浏览器时,按共享 > Gmail。系统会显示 Gmail 应用的写邮件屏幕。此时点按“最近”按钮会显示 Chrome 和 Gmail 作为单独的任务运行。(此时启动Gmail邮件Activity加了FLAG_ACTIVITY_NEW_TASK才会在最近任务列表中出现两个任务(栈),若不加这个FALG,以标准模式启动Gmail Activty,此时在最近任务屏幕只会有一个任务(栈),栈顶是邮件Activity,栈底是浏览器Activty)
2、什么是返回栈
返回栈是从用户使用的角度来进行定义的,返回栈中包含一个或多个任务栈,返回栈所表示的就是当用户不断回退页面时所能看到的一系列 Activity 的集合,这些页面可能是处于多个不同的任务栈中。但同时只会有一个任务栈够处于前台,只有处于前台任务栈的 Activity 才能与用户交互。
2.1 示例1
例如,用户先启动了应用 A,先后打开了 Activity 1 和 Activity 2,此时 Task A 是前台任务栈。之后用户又点击 Home 键回到了桌面,启动了应用 B,又先后打开了 的 Activity 3 和 Activity 4,此时 Task B 就成为了前台任务栈,Task A 成了后台任务栈。用户点击返回键的过程中依次展现的页面就会是 Activity 4 -> Activity 3 -> 桌面
而如果用户在打开应用 B 时并没有回到桌面,而是直接通过应用 A 启动了应用 B 的话,用户点击返回键的过程中依次展现的页面就会是 Activity 4 -> Activity 3 -> Activity 2 -> Activity 1 -> 桌面
而第一种情况中,返回栈只包含 Task B 一个任务栈,所以当 Task B 被清空后就会直接回到桌面。在第二种情况中,返回栈中包含 Task A 和 Task B 两个任务栈,所以当 Task B 被清空后也会先切回到 Task A,等到 Task A 也被清空后才会回到桌面
2.2 示例2
假设有三个应用,每个应用都有3个Activity,按首页,二级页,三级页面,分别为A,B,C。其中所有应用的根Activity-首页A都是singleTask,每个应用的C-Activity会启动下一个应用的首页A-Activity。最终这三个应用会分别起三个任务Task,每个任务中有三个Activity,此时它们的顺序如下:
app1_A->app1_B->app1_c->
app2_A->app2_B->app2_c->
app3_A->app3_B->app3_c
他们的task列表为:app1->app2->app3,如果不停的按下返回键,它们会按倒序进行退出。
我们把app2_B是设成singleTask的Activity(如下图上半部分标红位置 ,我们在app3_c中启动app2_B,这时就会强制把app2这个任务切到前台,并展示app2_B,因为在app2中,app2_C在app2_B的上面,所以app2_C会退栈销毁,这时app2_B就会显示在前台了。又因为app2被切换到前台,所以现在的task列表变成:
app1->app3->app2 所以在返回栈中activity的从底到顶顺序为:
app1_A->app1_B->app1_c->app3_A->app3_B->app3_c->app2_A->app2_B
当你按返回键时,会依次从后到前看到这些Activity。
3.任务栈的数据结构
Task 用数据结构 TaskRecord 表示。Task 中有一个 ActivityRecord 的集合 mActivities,它是以 Stack 的方式来管理其中的 Activity。先上一张数据结构的概览图:
总体架构:
图中的方框可以理解为一个中包含关系:譬如一个TaskRecord中包含多个ActivityRecord; 图中的连接线可以理解为等价关系,譬如同一个ActivityRecord会被TaskRecord和ProcessRecord引用,两者是从不同维度来管理ActivityRecord。
- ActivityRecord是Activity管理的最小单位,它对应着一个用户界面;
- TaskRecord也是一个栈式管理结构,每一个TaskRecord都可能存在一个或多个ActivityRecord,栈顶的ActivityRecord表示当前任务可见的界面;
- ActivityStack是一个栈式管理结构,每一个ActivityStack都可能存在一个或多个TaskRecord,栈顶的TaskRecord表示当前可见的任务;
- ActivityStackSupervisor管理着多个ActivityStack,但当前只会有一个获取焦点(Focused)的ActivityStack;
- ProcessRecord记录着属于一个进程的所有ActivityRecord,运行在不同TaskRecord中的ActivityRecord可能是属于同一个 ProcessRecord。
- 同一个TaskRecord的ActivityRecord可能分别处于不同的进程中,每个ActivityRecord所处的进程跟task没有关系
前台任务栈和后台任务栈:当有多个 Task 时,Android 系统只支持一个处于前台的 Task,其余的 Task 均处于后台,这些后台 Task 内部 Activity 保持顺序不变,用户可以一次将整 个Task 挪到后台或置为前台。
4.查看任务栈信息
查看任务栈命令:adb shell dumpsys activity activities
Task id #30
TaskRecord{7f2f34a #30 A=com.maweiqi.second U=0 sz=2}
Intent { flg=0x10000000 cmp=com.open.android.task1/.SecondActivity }
Hist #1: ActivityRecord{a0f9ded u0 com.open.android.task3/.OtherActivity t30}
Intent { flg=0x10400000 cmp=com.open.android.task3/.OtherActivity }
ProcessRecord{12090b5 27543:com.open.android.task3/u0a62}
Hist #0: ActivityRecord{1048af6 u0 com.open.android.task1/.SecondActivity t30}
Intent { flg=0x10000000 cmp=com.open.android.task1/.SecondActivity }
ProcessRecord{5bc013e 26035:com.open.android.task1/u0a59}
Task id #31
TaskRecord{dce52bb #31 A=com.open.android.task3 U=0 sz=1}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.open.android.task3/.MainActivity }
Hist #0: ActivityRecord{f9e58c5 u0 com.open.android.task3/.MainActivity t31}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.open.android.task3/.MainActivity }
ProcessRecord{12090b5 27543:com.open.android.task3/u0a62}
Task id #29
TaskRecord{5b063d8 #29 A=com.open.android.task1 U=0 sz=1}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.open.android.task1/.MainActivity }
Hist #0: ActivityRecord{689947d u0 com.open.android.task1/.MainActivity t29}
Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.open.android.task1/.MainActivity }
ProcessRecord{5bc013e 26035:com.open.android.task1/u0a59}
Running activities (most recent first):
TaskRecord{7f2f34a #30 A=com.maweiqi.second U=0 sz=2}
Run #3: ActivityRecord{a0f9ded u0 com.open.android.task3/.OtherActivity t30}
TaskRecord{dce52bb #31 A=com.open.android.task3 U=0 sz=1}
Run #2: ActivityRecord{f9e58c5 u0 com.open.android.task3/.MainActivity t31}
TaskRecord{7f2f34a #30 A=com.maweiqi.second U=0 sz=2}
Run #1: ActivityRecord{1048af6 u0 com.open.android.task1/.SecondActivity t30}
TaskRecord{5b063d8 #29 A=com.open.android.task1 U=0 sz=1}
Run #0: ActivityRecord{689947d u0 com.open.android.task1/.MainActivity t29}
mResumedActivity: ActivityRecord{a0f9ded u0 com.open.android.task3/.OtherActivity t30}
这些信息能够很好的反馈上面任务栈的数据结构。
5.TaskRecord属性
TaskRecord的职责是管理多个ActivityRecord,本文所述的任务、任务栈指的就是TaskRecord。 启动Activity时,需要找到Activity的宿主任务,如果不存在,则需要新建一个,也就是说所有的ActivityRecord都必须有宿主。 TaskRecord与ActivityRecord是一对多的关系,TaskRecord的属性中包含了ActivityRecord的数组; 同时,TaskRecord还需要维护任务栈本身的状态。
属性 | 描述 |
---|---|
taskid | TaskRecord的唯一标识 |
taskType | 任务栈的类型,等同于ActivityRecord的类型,是由任务栈的第一个ActivityRecord决定的 |
intent | 在当前任务栈中启动的第一个Activity的Intent将会被记录下来,后续如果有相同的Intent时,会与已有任务栈的Intent进行匹配,如果匹配上了,就不需要再新建一个TaskRecord了 |
realActivity, origActivity | 启动任务栈的Activity,这两个属性是用包名(CompentName)表示的,real和orig是为了区分Activity有无别名(alias)的情况,如果AndroidManifest.xml中定义的Activity是一个alias,则此处real表示Activity的别名,orig表示真实的Activity |
affinity | TaskRecord把Activity的affinity记录下来,后续启动Activity时,会从已有的任务栈中匹配affinity,如果匹配上了,则不需要新建TaskRecord |
rootAffinity | 记录任务栈中最底部Activity的affinity,一经设定后就不再改变 |
mActivities | 这是TaskRecord最重要的一个属性,TaskRecord是一个栈结构,栈的元素是ActivityRecord,其内部实现是一个数组mActivities |
stack | 当前TaskRecord所在的ActivityStack |
6.后记
后面文章将深入部分函数的代码来了解TaskRecord的工作原理及AMS其他数据结构。