SystemUI问题分析之Recents不显示TaskView

问题

操作描述:任意打开多个应用后切换到后台,点击最近任务图标显示后台应用为空。
出现机型:Android 8.1 Go版本
问题截图:


问题界面

分析过程

拿到这个问题,首先考虑的是有哪些情况会导致这个问题:
case A:没有获取到activity stack中的数据;
case B:数据获取到了,没有获取到各个task的快照;
case C:其他未知原因;
解决这个问题我们要了解recents获取最近任务数据的基本流程:


Recents数据获取

从时序上看,在RecentsTaskLoadPlan中获取stack中的app数据,preloadPlan的代码如下:

/**
  * Preloads the list of recent tasks from the system. After this call, the TaskStack will
  * have a list of all the recent tasks with their metadata, not including icons or
  * thumbnails which were not cached and have to be loaded.
  *
  * The tasks will be ordered by:
  * - least-recent to most-recent stack tasks
  * - least-recent to most-recent freeform tasks
  *
  * Note: Do not lock, since this can be calling back to the loader, which separately also drives
  * this call (callers should synchronize on the loader before making this call).
  */
void preloadPlan(RecentsTaskLoader loader, int runningTaskId,
            boolean includeFrontMostExcludedTask) {
        Resources res = mContext.getResources();
        ArrayList allTasks = new ArrayList<>();
        if (mRawTasks == null) {
            preloadRawTasks(includeFrontMostExcludedTask);
        }

        SparseArray affiliatedTasks = new SparseArray<>();
        SparseIntArray affiliatedTaskCounts = new SparseIntArray();
        SparseBooleanArray lockedUsers = new SparseBooleanArray();
        String dismissDescFormat = mContext.getString(
                R.string.accessibility_recents_item_will_be_dismissed);
        String appInfoDescFormat = mContext.getString(
                R.string.accessibility_recents_item_open_app_info);
        int currentUserId = mPreloadedUserId;
        long legacyLastStackActiveTime = migrateLegacyLastStackActiveTime(currentUserId);
        long lastStackActiveTime = Settings.Secure.getLongForUser(mContext.getContentResolver(),
                Settings.Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, legacyLastStackActiveTime, currentUserId);
        if (RecentsDebugFlags.Static.EnableMockTasks) {
            lastStackActiveTime = 0;
        }
        long newLastStackActiveTime = -1;
        int taskCount = mRawTasks.size();
        for (int i = 0; i < taskCount; i++) {
            ActivityManager.RecentTaskInfo t = mRawTasks.get(i);

            // Compose the task key
            Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, ActivityManager.RecentTaskInfo.getStackId(t), t.baseIntent,
            ActivityManager.RecentTaskInfo.getUserId(t), ActivityManager.RecentTaskInfo.getFirstActiveTime(t),
                    ActivityManager.RecentTaskInfo.getLastActiveTime(t));

            // This task is only shown in the stack if it satisfies the historical time or min
            // number of tasks constraints. Freeform tasks are also always shown.
            boolean isFreeformTask = SystemServicesProxy.isFreeformStack(ActivityManagerDelegator.RecentTaskInfoDelegator.getStackId(t));
            boolean isStackTask;
            if (Recents.getConfiguration().isGridEnabled) {
                // When grid layout is enabled, we only show the first
                // TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT} tasks.
                isStackTask = ActivityManager.RecentTaskInfo.getLastActiveTime(t) >= lastStackActiveTime &&
                    i >= taskCount - TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
            } else if (Recents.getConfiguration().isLowRamDevice) {
                // Show a max of 3 items
                isStackTask = ActivityManager.RecentTaskInfo.getLastActiveTime(t) >= lastStackActiveTime &&
                        i >= taskCount - TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
            } else {
                isStackTask = isFreeformTask || !isHistoricalTask(t) ||
                    (ActivityManager.RecentTaskInfo.getLastActiveTime(t) >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS));
            }
            boolean isLaunchTarget = taskKey.id == runningTaskId;

            // The last stack active time is the baseline for which we show visible tasks.  Since
            // the system will store all the tasks, we don't want to show the tasks prior to the
            // last visible ones, otherwise, as you dismiss them, the previous tasks may satisfy
            // the other stack-task constraints.
            if (isStackTask && newLastStackActiveTime < 0) {
                newLastStackActiveTime = ActivityManager.RecentTaskInfo.getLastActiveTime(t);
            }

            // Load the title, icon, and color
            ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
            String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
            String titleDescription = loader.getAndUpdateContentDescription(taskKey,
                    t.taskDescription, res);
            String dismissDescription = String.format(dismissDescFormat, titleDescription);
            String appInfoDescription = String.format(appInfoDescFormat, titleDescription);
            Drawable icon = isStackTask
                    ? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
                    : null;
            ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
                    false /* loadIfNotCached */, false /* storeInCache */);
            int activityColor = loader.getActivityPrimaryColor(t.taskDescription);
            int backgroundColor = loader.getActivityBackgroundColor(t.taskDescription);
            boolean isSystemApp = (info != null) &&
                    ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
            if (lockedUsers.indexOfKey(ActivityManager.RecentTaskInfo.getUserId(t)) < 0) {
                lockedUsers.put(ActivityManager.RecentTaskInfo.getUserId(t), Recents.getSystemServices().isDeviceLocked(ActivityManager.RecentTaskInfo.getUserId(t)));
            }
            boolean isLocked = lockedUsers.get(ActivityManager.RecentTaskInfo.getUserId(t));

            // Add the task to the stack
            Task task = new Task(taskKey, t.affiliatedTaskId, ActivityManager.RecentTaskInfo.getAffiliatedTaskColor(t), icon,
                    thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
                    activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
            ActivityManagerRecentTaskInfo.supportsSplitScreenMultiWindow(t), ActivityManager.RecentTaskInfo.bounds(t),
                    t.taskDescription, ActivityManager.RecentTaskInfo.resizeMode(t), t.topActivity, isLocked);

            allTasks.add(task);
            affiliatedTaskCounts.put(taskKey.id, affiliatedTaskCounts.get(taskKey.id, 0) + 1);
            affiliatedTasks.put(taskKey.id, taskKey);
        }
        if (newLastStackActiveTime != -1) {
            Recents.getSystemServices().updateOverviewLastStackActiveTimeAsync(
                    newLastStackActiveTime, currentUserId);
        }

        // Initialize the stacks
        mStack = new TaskStack();
        mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */);
}

可以看出mRawTask就是获取到的最近任务的list,我们在遍历该list之前,将list的size打印出来。打印出Log如下:

06-13 08:59:17.156 1011-2095/com.android.systemui V/monster: taskCount: 2

在此之前打开了settings 和 phone 应用,如上述log,recents已经获取到最近任务数据。接下来,需要查一下是否是没有获取到对应的task的快照呢?
其获取的地方在此处:

ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
                    false /* loadIfNotCached */, false /* storeInCache */);

/frameworks/base/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java

/**
     * Returns the cached thumbnail if the task key is not expired, updating the cache if it is.
     */
    synchronized ThumbnailData getAndUpdateThumbnail(Task.TaskKey taskKey, boolean loadIfNotCached,
            boolean storeInCache) {
        SystemServicesProxy ssp = Recents.getSystemServices();

        ThumbnailData cached = mThumbnailCache.getAndInvalidateIfModified(taskKey);
        if (cached != null) {
            return cached;
        }

        cached = mTempCache.getAndInvalidateIfModified(taskKey);
        if (cached != null) {
            mThumbnailCache.put(taskKey, cached);
            return cached;
        }

        if (loadIfNotCached) {
            RecentsConfiguration config = Recents.getConfiguration();
            if (config.svelteLevel < RecentsConfiguration.SVELTE_DISABLE_LOADING) {
                // Load the thumbnail from the system
                ThumbnailData thumbnailData = ssp.getTaskThumbnail(taskKey.id,
                        true /* reducedResolution */);
                if (thumbnailData.thumbnail != null) {
                    if (storeInCache) {
                        mThumbnailCache.put(taskKey, thumbnailData);
                    }
                    return thumbnailData;
                }
            }
        }

        // We couldn't load any thumbnail
        return null;
    }

所以需要在获取的地方判断获取的thumbnail是否为空?获取log如下:

06-13 09:30:33.900 1003-1251/com.android.systemui V/monster: thumbnail: true
06-13 09:30:36.669 1003-1003/com.android.systemui V/monster: thumbnail: true

可以看出,也获取到了对应的快照了。那么需要再去寻找其他可疑的地方。
从preload()函数中可以看到这么一段逻辑:

if (Recents.getConfiguration().isGridEnabled) {
                // When grid layout is enabled, we only show the first
                // TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT} tasks.
                isStackTask = ActivityManager.RecentTaskInfo.getLastActiveTime(t) >= lastStackActiveTime &&
                    i >= taskCount - TaskGridLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
            } else if (Recents.getConfiguration().isLowRamDevice) {
                // Show a max of 3 items
                isStackTask = ActivityManager.RecentTaskInfo.getLastActiveTime(t) >= lastStackActiveTime &&
                        i >= taskCount - TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
            } else {
                isStackTask = isFreeformTask || !isHistoricalTask(t) ||
                    (ActivityManager.RecentTaskInfo.getLastActiveTime(t) >= lastStackActiveTime && i >= (taskCount - MIN_NUM_TASKS));
            }

从上面可以看出在LowRam的机器上(Android Go)版本,google设计了这么一个逻辑,获取task的lastActiveTime,与一个阈值进行比较,如果小于这个阈值,则获取不到app的相关信息,并不让显示taskView。所以将这些时间打印出来,观察是否有无异常,log如下:

12-16 12:05:11.241 942-4048/com.android.systemui V/monster: legacyLastStackActiveTime: 0
    lastStackActiveTime: 1505966512318
12-16 12:05:11.246 942-4048/com.android.systemui V/monster: getLastActiveTime: 1450238711095

异常log中获取到了一些时间戳,转化成正常时间:

1505966512318 > 2017-09-21 12:01:52
1450238711095 > 2015-12-16 12:05:11

这里的阈值是1505966512318,而最近任务的lastActiveTime是1450238711095,前面有介绍到此逻辑:

else if (Recents.getConfiguration().isLowRamDevice) {
                // Show a max of 3 items
                isStackTask = ActivityManager.RecentTaskInfo.getLastActiveTime(t) >= lastStackActiveTime &&
                        i >= taskCount - TaskStackLowRamLayoutAlgorithm.MAX_LAYOUT_TASK_COUNT;
            } 

由于设备是Android Go,这里的isStackTask flag当前就是false了,而正是因为这个flag导致recents在处理的时候,认为当前获取的的recents task是非法的,故没有显示taskView。
之后经确认,当时问题发现者存在手动调动时间,导致获取的stack active time在阈值时间之前,联网校准时间后,该问题解决。

你可能感兴趣的:(SystemUI问题分析之Recents不显示TaskView)