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<Task> allTasks = new ArrayList<>();
        if (mRawTasks == null) {
            preloadRawTasks(includeFrontMostExcludedTask);
        }

        SparseArray<Task.TaskKey> 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在阈值时间之前,联网校准时间后,该问题解决。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,230评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,261评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,089评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,542评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,542评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,544评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,922评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,578评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,816评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,576评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,658评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,359评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,920评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,859评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,381评论 2 342

推荐阅读更多精彩内容