最近任务列表底层逻辑(一)-获取最近任务列表
前言
每一家手机厂商对getRecenTasks内部都进行了不同的插桩,并且可能会和上层systemui最近任务联动,
所以下面的代码不能保证在国内手机上表现一致
在最近任务中显示什么卡片,一般从上层桌面/最近任务中,向系统反射调用getRecentTask方法来获取
ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
boolean getTasksAllowed, int userId, int callingUid) {
return new ParceledListSlice<>(getRecentTasksImpl(maxNum, flags, getTasksAllowed,
userId, callingUid));
}
getRecentTasksImpl入口
private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
boolean getTasksAllowed, int userId, int callingUid) {
//获取调用者声明的flg,主要决定是否需要返回,声明了exclude_from_recent的task信息,是否需要返回
final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;
final Set<Integer> includedUsers = getProfileIds(userId);
includedUsers.add(Integer.valueOf(userId));
//准备一个列表作为最终结果返回
final ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<>();
final int size = mTasks.size();
int numVisibleTasks = 0;
for (int i = 0; i < size; i++) {
final Task task = mTasks.get(i);
if (isVisibleRecentTask(task)) {
numVisibleTasks++;
if (isInVisibleRange(task, i, numVisibleTasks, withExcluded)) {
// Fall through
} else {
// Not in visible range
continue;
}
} else {
// Not visible
continue;
}
// Skip remaining tasks once we reach the requested size
if (res.size() >= maxNum) {
continue;
}
// 分身空间下不会显示主空间的recenttask信息
// Only add calling user or related users recent tasks
if (!includedUsers.contains(Integer.valueOf(task.mUserId))) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + task);
continue;
}
if (task.realActivitySuspended) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + task);
continue;
}
if (!getTasksAllowed) {
// If the caller doesn't have the GET_TASKS permission, then only
// allow them to see a small subset of tasks -- their own and home.
if (!task.isActivityTypeHome() && task.effectiveUid != callingUid) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + task);
continue;
}
}
//autoRemove不显示 ,设置了FLAG_AUTO_REMOVE_FROM_RECENTS,并且task里没有不空的了
if (task.autoRemoveRecents && task.getTopNonFinishingActivity() == null) {
// Don't include auto remove tasks that are finished or finishing.
if (DEBUG_RECENTS) {
Slog.d(TAG_RECENTS, "Skipping, auto-remove without activity: " + task);
}
continue;
}
if ((flags & RECENT_IGNORE_UNAVAILABLE) != 0 && !task.isAvailable) {
if (DEBUG_RECENTS) {
Slog.d(TAG_RECENTS, "Skipping, unavail real act: " + task);
}
continue;
}
//一般如果用户用命令跳过了开机引导,会出现该问题
if (!task.mUserSetupComplete) {
// Don't include task launched while user is not done setting-up.
// NOTE: not guarding with DEBUG_RECENTS as it's not frequent enough to spam logcat,
// but is useful when running CTS.
Slog.d(TAG_RECENTS, "Skipping, user setup not complete: " + task);
continue;
}
res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed));
}
return res;
}
小结:
- 首先经过两层过滤,后面会详细分析
- 分手空间和主空间等,tasklist信息隔离,不可互相获取
- 设置了FLAG_AUTO_REMOVE_FROM_RECENTS,如果调用了finish,会自动从list清除
- usercomplete完毕前拿不到数据,跳过,该类问题上报常见在用户用命令跳过了开机引导
isVisibleRecentTask
第一层过滤,初步判断是否可见,
@VisibleForTesting
boolean isVisibleRecentTask(Task task) {
if (DEBUG_RECENTS_TRIM_TASKS) {
Slog.d(TAG, "isVisibleRecentTask: task=" + task
+ " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks
+ " sessionDuration=" + mActiveTasksSessionDurationMs
+ " inactiveDuration=" + task.getInactiveDuration()
+ " activityType=" + task.getActivityType()
+ " windowingMode=" + task.getWindowingMode()
+ " isAlwaysOnTopWhenVisible=" + task.isAlwaysOnTopWhenVisible()
+ " intentFlags=" + task.getBaseIntent().getFlags());
}
switch (task.getActivityType()) {
case ACTIVITY_TYPE_HOME:
case ACTIVITY_TYPE_RECENTS:
case ACTIVITY_TYPE_DREAM:
// Ignore certain activity types completely
return false;
case ACTIVITY_TYPE_ASSISTANT:
// Ignore assistant that chose to be excluded from Recents, even if it's a top
// task.
if ((task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
== FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) {
return false;
}
break;
}
// Ignore certain windowing modes
switch (task.getWindowingMode()) {
case WINDOWING_MODE_PINNED:
return false;
case WINDOWING_MODE_MULTI_WINDOW:
// Ignore tasks that are always on top
if (task.isAlwaysOnTopWhenVisible()) {
return false;
}
break;
}
// If we're in lock task mode, ignore the root task
if (task == mService.getLockTaskController().getRootTask()) {
return false;
}
// Ignore the task if it is started on a display which is not allow to show its tasks on
// Recents.
if (task.getDisplayContent() != null
&& !task.getDisplayContent().canShowTasksInHostDeviceRecents()) {
return false;
}
return true;
}
小结:
不可见的:
- 桌面/最近任务/Dream(没遇到过)/语音助手-设置了excludefromrecent
- pin始终头部可见,及分屏的可见那一半
- lockmode锁定模式,这种情况下不解锁进不去最近任务
- 虚拟设备作限制
isInVisibleRange
第二层限制,visible范围
private boolean isInVisibleRange(Task task, int taskIndex, int numVisibleTasks,
boolean skipExcludedCheck) {
//如果调用方声明了要拿取最近任务中不可见的task,需要保证返回的列表头部即使不可见也要返回
if (!skipExcludedCheck) {
// Keep the most recent task of home display even if it is excluded from recents.
final boolean isExcludeFromRecents =
(task.getBaseIntent().getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
== FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
if (isExcludeFromRecents) {
if (DEBUG_RECENTS_TRIM_TASKS) {
Slog.d(TAG,
"\texcludeFromRecents=true,"
+ " taskIndex: " + taskIndex
+ " getTopVisibleActivity: " + task.getTopVisibleActivity()
+ " isOnHomeDisplay: " + task.isOnHomeDisplay());
}
// The Recents is only supported on default display now, we should only keep the
// most recent task of home display.
boolean isMostRecentTask;
if (enableRefactorTaskThumbnail()) {
isMostRecentTask = task.getTopVisibleActivity() != null;
} else {
isMostRecentTask = taskIndex == 0;
}
return (task.isOnHomeDisplay() && isMostRecentTask);
}
}
//不同的设备根据性能指标,可能会制定·最近任务现显示的个数,限制返回list的个数在min和max范围内
if (mMinNumVisibleTasks >= 0 && numVisibleTasks <= mMinNumVisibleTasks) {
// Always keep up to the min number of recent tasks, after that fall through to the
// checks below
return true;
}
//保持pip的父节点可见,具体场景,腾讯视频画中画
// The given task if always treated as in visible range if it is the origin of pinned task.
if (task.mChildPipActivity != null) return true;
//同上,限制在max以内
if (mMaxNumVisibleTasks >= 0) {
// Always keep up to the max number of recent tasks, but return false afterwards
return numVisibleTasks <= mMaxNumVisibleTasks;
}
//保活,
if (mActiveTasksSessionDurationMs > 0) {
// Keep the task if the inactive time is within the session window, this check must come
// after the checks for the min/max visible task range
if (task.getInactiveDuration() <= mActiveTasksSessionDurationMs) {
return true;
}
}
return false;
}
小结:
- 设定要拿取exlcudetRecenTask时,即使list头部设置了excludeFromRecent,也会返回一个,这一般是为了保证用户第一眼看到的可见,例如上划进入最近任务
- 在mMinNumVisibleTasks和mMaxNumVisibleTasks之间,
- mActiveTasksSessionDurationMs,保活时间内,这个数值和上面的min/max都是在prop中指定的,根据不同机器性能指定
- pip的父信息,例如腾讯视频进入画中画,最近任务中仍然会显示全屏的taskSnapshot
总结
经过两层过滤后,再加上一些限制条件,过滤除了最终的recentTasklist信息,
=====
todo:
下一节,recentasklist的添加和移除