在 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 ; if (newTask) { final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null ) ? mSourceRecord.getTask() : null ; setNewTask(taskToAffiliate); } else if (mAddingToTask) { 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 () { 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) { if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) { intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info, mStartActivity.isActivityTypeHome()); }else { intentActivity = mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea); } } return intentActivity != null ? intentActivity.getTask() : null ; }
关键点1 :会判断有没有给Flag设置FLAG_ACTIVITY_NEW_TASK
或者启动模式 是不是SINGLE_INSTANVE || SINGLE_TASK
,如果符合上述条件就会将 putIntoExistingTask
设置为true.
关键点2 : 这里会进行两层判断:
启动模式是 LAUNCH_INSTANCE
启动模式不是 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); 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); 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; }
整个查找过程就是调用 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(); 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) { 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) { 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) { 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 ; }
现在回到 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 ) { ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: voice session" , task); return false ; } if (task.mUserId != userId) { ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: different user" , task); return false ; } final ActivityRecord r = task.getTopNonFinishingActivity(false ); 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() : "" )); 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,那么你就会看到下面这个情况: