【Android源码-AMS】(五) TaskRecord

注:本文作者Nemo, http://blog.csdn.net/nemo__
 
 

一、包名

路径:frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
包名:com.android.server.am.TaskRecord
父类:无
子类:无(final型)
 

二、概述

SDK/docs/guide/components/tasks-and-back-stack.html  
A task is a collection of activities that users interact with when performing a certain job. 
The activities are arranged in a stack (the back stack), in the order in which each activity is opened.

       TaskRecord是一个虚拟的概念,它记录着启动的所有Activity序列,以及用户在按HOME键、Back键后,Activity界面跳转规则,这个栈被称为back stack。它的成员ArrayList<ActivityRecord> mActivities实现后进先出的跳转规则,位于top的ActivityRecord会在列表的末尾。

       设备的桌面(HOME)是绝大多数Task启动的地方。当用户点击桌面某个应用图标时,以这个应用为root的Task会被拉到前台(Task顶部可能是其它Activity);如果没有这个应用的Task,会创建一个以此应用主Activity为root的Task。

       在Task中的所有ActivityRecord,从来都不会重排序,只会进栈和出栈,遵循”后进先出”的原则。当一个Task中所有ActivityRecord均被remove时,这个栈也会被销毁。

       Activity和TaskRecord默认情况下行为规则:

(1) ActivityA启动ActivityB,ActivityA状态stopped,系统会保持它的状态(滑块滚动位置、文本框输入等),当用户点击Back键时,ActivityA的状态会被恢复。

(2) 当用户按HOME键回到桌面时,当前Activity会被stop,它的Task将变为后台,系统会保存这个Task中的每个Activity的状态,如果用户再次桌面点击这个Task的根Activity对应的应用图标时,这个Task将会被resume,这个Task的顶部Activity会被resume。

(3) 当用户按Back键,当前Activity会出栈并且被destroy,前一个Activity会resume。系统不会保存被destroy的Activity状态。用户一直按Back键,Task会一直出栈直到回到桌面或启动这个Task的Activity

(4) 一个standard属性的Activity可以被实例化多次,在不同的TaskRecord中。虽然一个Activity可以被启动多次,但过多的后台会被系统清理掉,那么destroy后的Activity状态就会丢失。当你需要一个被意外destroy的Activity保持之前的状态时,可以重写onSaveInstanceState()方法

 
1. android:launchMode

SDK/docs/guide/topics/manifest/activity-element.html#lmode 

android:launchMode属性的四项值:
standard
singleTop
singleTask
singleInstance

       默认为standard的。standardsingleTop启动模式的Activity,可能有多个实例,并存在于不同的栈中;而singleTasksingleInstance启动模式的Activity只会存一个实例。

       standard 启动模式的Activity,不管所有栈有多少个实例,均会创建新的Activity实例,并压入当前栈顶,每个Activity实例对应一个新的Intent。

       singleTop 启动模式的Activity,会创建新的Activity实例,如果目标栈栈顶是当前Activity的实例时,那么栈顶那个已存在的Activity实例会接收此Intent(在onNewIntent()方法),并不创建新的实例。但是如果某个栈顶已存在singleTop的Activity的实例,但这个栈不是目标栈时,也会创建新的实例在目标栈。

       singleTask 启动模式的Activity,会先在系统中查找属性值affinity等于它的属性值taskAffinity的TaskRecord(一个TaskRecord在创建时初始化其affinity,使用root Activity的taskAffinity,如果未定义就使用Application的taskAffinity,都未定义则使用包名);如果不存在这样的TaskRecord会创建一个taskAffinity的TaskRecord,并作为root;如果存在这样的TaskRecord,会看这个任务中是否已有要启动的Activity实例,如果有就销毁此实例以上的所有其它项,复用已存在的实例(onNewIntent()方法),最终该实例会位于栈顶,如果不存在有要启动的Activity实例,会要任务栈顶创建一个新的要启动的Activity实例。所以可以理解为singleTask型的Activity只有一个实例,因为它总会去找taskAffinity定义的Task。

       singleInstance 启动模式的Activity,只会保留一个该Activity实例,该栈有且只有一个Activity实例;并且由它启动的新的Activity会在其它栈中。

上面这些在AndroidManifest.xml中定义的launchMode属性,会被startActivity时在Intent设置的flag覆盖。

可以覆盖默认设置的launchMode属性的flag有:

(1) FLAG_ACTIVITY_NEW_TASK
它的作用和设置launchMode属性为singleTask一致。

(2) FLAG_ACTIVITY_SINGLE_TOP
它的作用和设置launchMode属性为singleTop一致。

(3) FLAG_ACTIVITY_CLEAR_TOP
没有对应的launchMode属性。如果当前栈中有一个当前Activity在运行,那么会清除运行的Activity上部的所有其它项,并且resume它通过onNewIntent()方法。FLAG_ACTIVITY_CLEAR_TOP经常是和FLAG_ACTIVITY_NEW_TASK一起使用,用来定位一个在其它栈中已运行的Activity。

 
2. android:taskAffinity

The task that the activity has an affinity for. Activities with the same affinity conceptually belong to the same task (to the same “application” from the user’s perspective). The affinity of a task is determined by the affinity of its root activity.

The affinity determines two things: the task that the activity is re-parented to(allowTaskReparenting attribute), and the task that will house the activity when it is launched with the FLAG_ACTIVITY_NEW_TASK flag.

       taskAffinity定义了Activity间的关联程度。相同affinity的Activity位于同一Task(在用户视角中),不过一定成立的只有singleTasksingleInstance只有singleTask模式会关心这个属性的配置。一个Task的affinity是由它的根Activity决定的。

       taskAffinity决定两件事:一是设置allowTaskReparenting为true的Activity,在它所在的Task后台后,该Activity实例要re-parented-to到哪个Task;二是FLAG_ACTIVITY_NEW_TASKsingleTask启动的Activity会位于哪个Task内。

       默认情况下,同一个应用的所有Activity有相同的affinity–应用包名。如果标签未定义taskAffinity,则使用标签的;若标签同样未定义,则使用包名。当然也可以设置taskAffinity为空字符串,来表明它不关联于任何一个Task。

 
3. android:allowTaskReparenting

       allowTaskReparenting触发时机在特定affinity的Task在转向前台时,属性taskAffinity为affinity的Activity,且allowTaskReparenting为true

       只会作用于launchMode为standardsingleTop模式(singleTask总是在特定affinity的Task,而singleInstance的Activity只会在栈底,这样task的affinity也与Activity的相同),默认为false。

       ActivityA启动ActivityB(standardsingleTop型),ActivityB会位于ActivityA所在的Task,当ActivityB定义的taskAffinity的那个Task要转到前台时,如果设置了android:allowTaskReparenting=true,则会把新启动的ActivityB实例重定向到ActivityB定义的taskAffinity的那个Task中。原有的ActivityA的Task中这个ActivityB实例会出栈。

For example, if an e-mail message contains a link to a web page, clicking the link brings up an activity that can display the page. That activity is defined by the browser application, but is launched as part of the e-mail task. If it’s reparented to the browser task, it will be shown when the browser next comes to the front, and will be absent when the e-mail task again comes forward.

 
4. android:alwaysRetainTaskState

Whether or not the state of the task that the activity is in will always be maintained by the system — “true” if it will be, and “false” if the system is allowed to reset the task to its initial state in certain situations. The default value is “false”. This attribute is meaningful only for the root activity of a task; it’s ignored for all other activities.

Normally, the system clears a task (removes all activities from the stack above the root activity) in certain situations when the user re-selects that task from the home screen. Typically, this is done if the user hasn’t visited the task for a certain amount of time, such as 30 minutes.

       alwaysRetainTaskState触发时机在系统清理后台Task,且Activity实例为根Activity时

       默认值为false。默认情况下,当用户从桌面上点击启动一个应用时,如果这个应用Activity在很长一段时间内都没有访问过(如30分钟)时,会清空此Task中所有Activity,并重新启动这个Activity。

       若设置为true,则在这种情况下会保留这个栈中根Activity的状态,比较有用在Web浏览器等。alwaysRetainTaskState只会对Task中根Activity有意义

 
5. android:finishOnTaskLaunch

Whether or not an existing instance of the activity should be shut down (finished) whenever the user again launches its task (chooses the task on the home screen) — “true” if it should be shut down, and “false” if not. The default value is “false”.

If this attribute and allowTaskReparenting are both “true”, this attribute trumps the other. The affinity of the activity is ignored. The activity is not re-parented, but destroyed.

       finishOnTaskLaunch触发时机为标注finishOnTaskLaunchTask为true的Activity所在的Task在桌面被重新选择时(chooses the task on the home screen)

       默认值为false。如果设置为true,那么在此Activity所在的Task重新启动时(比如桌面上点击某一个应用图标),会销销毁此Task中的该Activity实例。如果allowTaskReparenting也设置为true,则以finishOnTaskLaunch为准,会执行销毁(它们均在某一特定Task转向前台时进行)。

 
6. android:clearTaskOnLaunch

       clearTaskOnLaunch触发时机在标注clearTaskOnLaunch为true的Activity为某一Task的根Activity,不论是HOME键还是Back键离开该Task时。

       默认为false。它只会作用于某一Task的根Activity。当用户点击HOME键使得当前Task转为后台时,就会清理根Activity以上的所有Activity实例,下一次进入根Activity,就只看到根Activity一个。如果在清理的Activity实例中包含allowTaskReparenting设置为true的Activity实例,则会先执行re-parent-to,再清理其它的Activity实例。

 
根据查看 adb shell dumpsys activity activities 命令的源码实现ActivityManagerService.dumpActivitiesLocked()可知,AMS中关于ActivityRecord,ActivityStack,TaskRecord,ActivityDisplay的关系,可知:

for(ActivityDisplay activityDisplay : mActivityDisplays)    --- ActivityDisplay   --- Display #0
    for(ActivityStack stack : activityDisplay.mStacks)      --- ActivityStack     --- Stack #0
        for(TaskRecord task : stack.mTaskHistory)           --- TaskRecord        --- Task id #8
            for(ActivityRecord r : task.mActivities)        --- ActivityRecord    --- Hist #0

 
adb shell dumpsys activity recents,源码实现ActivityManagerService.dumpRecentsLocked():

for(TaskRecord task : mRecentTasks)                         --- TaskRecord        --- Recent #0

 
对于TaskRecord task,外部的调用可能为:

TaskRecord task = null; 

 

三、主要成员

  1. final int taskId;
    当前TaskRecord对应的id,具有唯一性,只在构造函数中初始化。

  2. String affinity;
    当前TaskRecord的affinity名字,在构造函数或setIntent()中赋值,可能为null,外部较多使用。

  3. String rootAffinity;
    rootAffinity只初始化一次,对应TaskRecord创建时,外部只在ActivityStack.findTaskLocked()中被引用。

  4. Intent intent;
    启动当前Task的Intent,只在构造函数或setIntent()中赋值,外部使用较多。

  5. Intent affinityIntent;
    只在私有构造函数中被初始化,最终只在restoreFromXml()中会调用此构造函数。

  6. int effectiveUid;
    task的当前有效的uid,外部多用它与callingUid比较是否相等,。

  7. ComponentName origActivity;

  8. ComponentName realActivity;

  9. final ArrayList<ActivityRecord> mActivities;

  10. ActivityStack stack;

  11. int taskType;

  12. boolean isPersistable = false;

  13. int mAffiliatedTaskId;

  14. int mAffiliatedTaskColor;

  15. TaskRecord mPrevAffiliate;

  16. int mPrevAffiliateTaskId = INVALID_TASK_ID;

  17. TaskRecord mNextAffiliate;

  18. int mNextAffiliateTaskId = INVALID_TASK_ID;

  19. int mCallingUid;

  20. String mCallingPackage;

  21. final ActivityManagerService mService;

  22. Rect mBounds = null;

  23. int mMinWidth;

  24. int mMinHeight;

task会在ActivityStarter中被赋值,四个方法setTaskFromReuseOrCreateNewTask()、setTaskFromSourceRecord()、setTaskFromInTask()、setTaskToCurrentTopOrCreateNewTask()中会调用mStartActivity.setTask(),而这个mStartActivity在startActivityUnchecked()进而在setInitialState()被赋值,它正是在startActivityLocked()中创建的那个ActivityRecord。

 ↓
【ActivityStarter】[startActivityLocked()()]
 ↓
【ActivityStarter】[startActivityUnchecked()]
 ↓
【ActivityStarter】[setInitialState()]
 ↓
 mStartActivity = r;

【ActivityStarter】[startActivityUnchecked()]
 ↓
【ActivityStarter】[setTaskFromReuseOrCreateNewTask()]
 ↓
【ActivityRecord】[setTask()]
 ↓
 task = newTask;
 ....

 

四、主要方法

TaskRecord()
构造函数,初始化大多数成员变量,有三处会new TaskRecord()
(1) TaskRecord.restoreFromXml(),此方法是恢复xml保存的Task信息调用的方法,会读取xml数据创建TaskRecord实例。

绑定到TaskRecord的过程
(1) 当启动Activity的flag中含有
 

五、总结

  1. taskAffinity决定两件事:一是设置allowTaskReparenting为true的Activity,在它所在的Task后台后,该Activity实例要re-parented-to到哪个Task;二是FLAG_ACTIVITY_NEW_TASK或singleTask启动的Activity会位于哪个Task内。只有singleTask模式会关心这个属性的配置。
    原文作者:Nemo__
    原文地址: https://blog.csdn.net/nemo__/article/details/53509424
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞