从源码的角度解析:Android启动模式和FLAG和taskAffinity的关系

在 Android 中存在 Task 的概念,启动的 Activity 都会被放入对应的 Task 中.那什么时候会去创建新的 Task 呢?

可能你会想到使用 Intent.FLAG_ACTIVITY_NEW_TASK,标志位来做,但是实际情况是通过他一个标志位并不行.

上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ActivityStarter{
int startActivityInner(ActivityRecord r, ActivityRecord sourceRecord,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
int startFlags, boolean doResume, ActivityOptions options, Task inTask,
boolean restrictedBgActivity, NeededUriGrants intentGrants){
// 这个方法很关键,通过标志位判断情况
Task reusedTask = getReusableTask();
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
// 判断是否需要新建新的task
if (newTask) {
final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
? mSourceRecord.getTask() : null;
setNewTask(taskToAffiliate);
} else if (mAddingToTask) {
//加入到已经存在的task
addOrReparentStartingActivity(targetTask, "adding to task");
}
}
}

这里要搞懂 reusedTask 和 targetTask 的关系.

  • reusedTask表示可复用的task,指的是系统已经存在的task,可以用来放目标activity.
  • targetTask表示目标activity最终会被放入的task.

reusedTask不会为空的情况是,是当你启动目标Activity的时候,对启动有要求的情况,比如启动模式为 singleInstance ,这个时候就要求去创建新的task,所以系统会先去找可复用的task.

这里来看getReusableTask() 很关键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private Task getReusableTask() {
// 关键点1:
boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
ActivityRecord intentActivity = null;
if (putIntoExistingTask) {
// 关键点2:
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
}else {
// 关键点3:
intentActivity =
mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
}
}
return intentActivity != null ? intentActivity.getTask() : null;
}

关键点1:会判断有没有给Flag设置FLAG_ACTIVITY_NEW_TASK 或者启动模式 是不是SINGLE_INSTANVE || SINGLE_TASK,如果符合上述条件就会将 putIntoExistingTask设置为true.

关键点2: 这里会进行两层判断:

  1. 启动模式是 LAUNCH_INSTANCE
  2. 启动模式不是 LAUNCH_INSTANCE

上面两者查找可复用task的方式是不用的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class RootWindowContainer{
ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info,
preferredTaskDisplayArea);
}
ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
TaskDisplayArea preferredTaskDisplayArea) {
ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s"
+ ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info,
preferredTaskDisplayArea);
mTmpFindTaskResult.init(activityType, taskAffinity, intent, info);

// Looking up task on preferred display area first
ActivityRecord candidateActivity = null;
if (preferredTaskDisplayArea != null) {
mTmpFindTaskResult.process(preferredTaskDisplayArea);
if (mTmpFindTaskResult.mIdealRecord != null) {
return mTmpFindTaskResult.mIdealRecord;
} else if (mTmpFindTaskResult.mCandidateRecord != null) {
candidateActivity = mTmpFindTaskResult.mCandidateRecord;
}
}

final ActivityRecord idealMatchActivity = getItemFromTaskDisplayAreas(taskDisplayArea -> {
if (taskDisplayArea == preferredTaskDisplayArea) {
return null;
}

mTmpFindTaskResult.process(taskDisplayArea);
if (mTmpFindTaskResult.mIdealRecord != null) {
return mTmpFindTaskResult.mIdealRecord;
}
return null;
});
if (idealMatchActivity != null) {
return idealMatchActivity;
}

if (WM_DEBUG_TASKS.isEnabled() && candidateActivity == null) {
ProtoLog.d(WM_DEBUG_TASKS, "No task found");
}
return candidateActivity;
}
}

通过上面代码我们可以总结发现:
是否需要查找可复用的条件是你有需要创建task的动作的时候.
比如 启动模式是 SINGLE_INSTANCE 或者是 SINGLE_TASK. 或者 FLAG设置了 FLAG_ACTIVITY_NEW_TASK.查找可复用的 Task的方式根据启动方式的不同有区别:

  • 当启动模式是: SINGLE_INSTANCE.
  • 当启动模式是: STANDARD SINGLETOP SINGLETASK.

情况1: 是 SINGLE_INSTANCE:

1
2
3
4
if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
mStartActivity.isActivityTypeHome());
}

过程就是遍历所有的 TASK 找到和要启动的目标一样的 ActivityRecord

情况2: 是 另外三种:

1
intentActivity =  mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);

这个是去寻找最合适的栈,意思就是如果能找和他一样的实例,那就是最好的栈,如果找不到,就找那个最适合让他入栈的task,什么叫最好的栈,比如 A 跳转 B ,用户按返回键是返回A,而不是跳到别的地方去了,保证用户操作的流畅性.

现在分析下情况2的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
TaskDisplayArea preferredTaskDisplayArea) {
ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s"
+ ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info,
preferredTaskDisplayArea);
mTmpFindTaskResult.init(activityType, taskAffinity, intent, info);

// Looking up task on preferred display area first
ActivityRecord candidateActivity = null;
if (preferredTaskDisplayArea != null) {
mTmpFindTaskResult.process(preferredTaskDisplayArea);
if (mTmpFindTaskResult.mIdealRecord != null) {
return mTmpFindTaskResult.mIdealRecord; // 这里返回的话,说明在已经存在的 task里面找到了相同的实例,也就是他是最合适的栈
} else if (mTmpFindTaskResult.mCandidateRecord != null) {
// mCandidateRecord意思就是返回适合的栈
candidateActivity = mTmpFindTaskResult.mCandidateRecord;
}
}

final ActivityRecord idealMatchActivity = getItemFromTaskDisplayAreas(taskDisplayArea -> {
if (taskDisplayArea == preferredTaskDisplayArea) {
return null;
}

mTmpFindTaskResult.process(taskDisplayArea);
if (mTmpFindTaskResult.mIdealRecord != null) {
return mTmpFindTaskResult.mIdealRecord;
}
return null;
});
if (idealMatchActivity != null) {
return idealMatchActivity;
}

if (WM_DEBUG_TASKS.isEnabled() && candidateActivity == null) {
ProtoLog.d(WM_DEBUG_TASKS, "No task found");
}
return candidateActivity;
}

整个查找过程就是调用 FindTaskResult.process方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class FindTaskResult{
void process(WindowContainer parent) {
cls = mIntent.getComponent();
if (mInfo.targetActivity != null) {
cls = new ComponentName(mInfo.packageName, mInfo.targetActivity);
}
userId = UserHandle.getUserId(mInfo.applicationInfo.uid);
isDocument = mIntent != null & mIntent.isDocument();
// If documentData is non-null then it must match the existing task data.
documentData = isDocument ? mIntent.getData() : null;

ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of %s in %s", mInfo,
parent);
parent.forAllLeafTasks(this);
}
}
class WindowContainer{
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).forAllLeafTasks(callback)) {
return true;
}
}
return false;
}
}
class Task{
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
boolean isLeafTask = true;
for (int i = mChildren.size() - 1; i >= 0; --i) {
// 关键代码 标记1
final Task child = mChildren.get(i).asTask();
if (child != null) {
isLeafTask = false;
if (child.forAllLeafTasks(callback)) {
return true;
}
}
}
if (isLeafTask) {
return callback.apply(this);
}
return false;
}
}

看这里的代码我们需要有个概念: Android的窗口机制.
所有的窗口父类都是WindowContainer,窗口的分类有

  • RootWindowContainer
  • DisplayContent
  • TaskDisplayArea
  • Task
  • ActivityRecord
  • WindowToken
  • WindowState

他们的关系是:
RootWindowContainer管理所有的DisplayContent,DisplayContent里面放着所有的TaskDisplayArea, TaskDisplayArea放着所有的Task, Task里面放着ActivtityRecord.

现在看回上面代码:

1
2
3
4
5
6
7
8
9
10
class WindowContainer{
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).forAllLeafTasks(callback)) {
return true;
}
}
return false;
}
}

过程就是遍历所有的孩子,直至找到叶子节点,也就是ActivityRecord,为什么这里我要说叶子节点是ActivityRecord呢,因为所有窗口类,只有 Task 重写了 findAllLeafTasks 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Task{
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
boolean isLeafTask = true;
for (int i = mChildren.size() - 1; i >= 0; --i) {
// 关键代码 标记1
final Task child = mChildren.get(i).asTask();
if (child != null) {
isLeafTask = false;
if (child.forAllLeafTasks(callback)) {
return true;
}
}
}
if (isLeafTask) {
return callback.apply(this);
}
return false;
}
}

然后看 标记1 的地方,调用了 asTask() 方法, asTask() 方法只有 Task类进行重载,返回了自己,其他的类比如ActivityRecord没有重载asTask,默认返回null.所以,当asTask返回的不是null,就说明还没到叶子节点,如果是叶子节点就返回null,然后执行callback.apply方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
boolean forAllLeafTasks(Function<Task, Boolean> callback) {
boolean isLeafTask = true;
for (int i = mChildren.size() - 1; i >= 0; --i) {
// 只有task类的 asTask返回的不是null
final Task child = mChildren.get(i).asTask();
if (child != null) {
isLeafTask = false;
if (child.forAllLeafTasks(callback)) {
return true;
}
}
}
//是叶子了
if (isLeafTask) {
//apply方法传入的参数就是 task当前 task对象
return callback.apply(this);
}
return false;
}

现在回到 RootWindowContainer看看 callback.apply方法是如何实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class RootWindowContainer{
public Boolean apply(Task task) {
if (!ConfigurationContainer.isCompatibleActivityType(mActivityType,
task.getActivityType())) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping task: (mismatch activity/task) %s", task);
return false;
}

if (task.voiceSession != null) {
// We never match voice sessions; those always run independently.
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: voice session", task);
return false;
}
if (task.mUserId != userId) {
// Looking for a different task.
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: different user", task);
return false;
}

// Overlays should not be considered as the task's logical top activity.
final ActivityRecord r = task.getTopNonFinishingActivity(false /* includeOverlays */);
if (r == null || r.finishing || r.mUserId != userId
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch root %s", task, r);
return false;
}
if (!ConfigurationContainer.isCompatibleActivityType(r.getActivityType(),
mActivityType)) {
ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch activity type", task);
return false;
}

final Intent taskIntent = task.intent;
final Intent affinityIntent = task.affinityIntent;
final boolean taskIsDocument;
final Uri taskDocumentData;
if (taskIntent != null && taskIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = taskIntent.getData();
} else if (affinityIntent != null && affinityIntent.isDocument()) {
taskIsDocument = true;
taskDocumentData = affinityIntent.getData();
} else {
taskIsDocument = false;
taskDocumentData = null;
}

ProtoLog.d(WM_DEBUG_TASKS, "Comparing existing cls=%s /aff=%s to new cls=%s /aff=%s",
r.getTask().rootAffinity, mIntent.getComponent().flattenToShortString(),
mInfo.taskAffinity, (task.realActivity != null
? task.realActivity.flattenToShortString() : ""));
// TODO Refactor to remove duplications. Check if logic can be simplified.
if (task.realActivity != null && task.realActivity.compareTo(cls) == 0
&& Objects.equals(documentData, taskDocumentData)) {
mIdealRecord = r;
return true;
} else if (affinityIntent != null && affinityIntent.getComponent() != null
&& affinityIntent.getComponent().compareTo(cls) == 0 &&
Objects.equals(documentData, taskDocumentData)) {
mIdealRecord = r;
return true;
} else if (!isDocument && !taskIsDocument
&& mIdealRecord == null && mCandidateRecord == null
&& task.rootAffinity != null) {
if (task.rootAffinity.equals(mTaskAffinity)) {
mCandidateRecord = r;
}
} else {
ProtoLog.d(WM_DEBUG_TASKS, "Not a match: %s", task);
}

return false;
}
}

我们来看下他是怎么判断什么是 最合适的 ActivityRecord, 什么是 比较合适的 ActivityRecord.

还有一篇比较好的博客推荐

在我们手机上你按Home键查看任务列表的时候,他是根据 Task 来展示展示的,比如你的MainActivity在task1 TwoActiivty在task2,那么你就会看到下面这个情况: