最近任务列表底层逻辑(一)-获取最近任务列表

500 阅读4分钟

最近任务列表底层逻辑(一)-获取最近任务列表

前言

每一家手机厂商对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;
    }
​

小结:

  1. 首先经过两层过滤,后面会详细分析
  2. 分手空间和主空间等,tasklist信息隔离,不可互相获取
  3. 设置了FLAG_AUTO_REMOVE_FROM_RECENTS,如果调用了finish,会自动从list清除
  4. 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;
    }

小结:

不可见的:

  1. 桌面/最近任务/Dream(没遇到过)/语音助手-设置了excludefromrecent
  2. pin始终头部可见,及分屏的可见那一半
  3. lockmode锁定模式,这种情况下不解锁进不去最近任务
  4. 虚拟设备作限制

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;
}

小结:

  1. 设定要拿取exlcudetRecenTask时,即使list头部设置了excludeFromRecent,也会返回一个,这一般是为了保证用户第一眼看到的可见,例如上划进入最近任务
  2. 在mMinNumVisibleTasks和mMaxNumVisibleTasks之间,
  3. mActiveTasksSessionDurationMs,保活时间内,这个数值和上面的min/max都是在prop中指定的,根据不同机器性能指定
  4. pip的父信息,例如腾讯视频进入画中画,最近任务中仍然会显示全屏的taskSnapshot

总结

经过两层过滤后,再加上一些限制条件,过滤除了最终的recentTasklist信息,

=====

todo:

下一节,recentasklist的添加和移除