Android LowRam设备显示最近应用个数的限制

背景

        Android12项目中,我们的LowRam设备被测试组报了个bug,说打开了十几个应用,但最近应用列表中最多只能有9个应用历史,而对比机pixel上则无此限制。

        最初我以为是Launcher3的问题,因为launcher3分了好几个版本,其中就有针对GO设备的,因此从launcher3上入手追查了一下。从显示最近应用历史的view上入手,到获取最近应用列表的路线查。

分析

        先从launcher3的RecentTasksList代码上入手,launcher显示最近应用列表的来源在:

// packages/apps/Launcher3/quickstep/src/com/android/quickstep/RecentTasksList.java   
 @VisibleForTesting
    TaskLoadResult loadTasksInBackground(int numTasks, int requestId, boolean loadKeysOnly) {
        int currentUserId = Process.myUserHandle().getIdentifier();
        List<ActivityManager.RecentTaskInfo> rawTasks =
                mActivityManagerWrapper.getRecentTasks(numTasks, currentUserId);
        // The raw tasks are given in most-recent to least-recent order, we need to reverse it
        Collections.reverse(rawTasks);
        ……
  }

        RecentTasksList调用loadTasksInBackground在后台从AMS中获取RecentTasks,所以最近应用列表实际上是AMS进行管理的,这个非常好理解,毕竟AMS是所有Activity的管理者,最近所启动的应用在AMS中必定能监控到。

        getRecentTasks方法第一个参数是获取最近应用的个数,那么最初是怀疑此处numTasks的值限制了low ram设备的launcher3最多只能显示9个最近应用,因此查了一下这个参数的来源,其来源如下:

 

// packages/apps/Launcher3/quickstep/src/com/android/quickstep/RecentTasksList.java   
    public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
        // Kick off task loading in the background
        mLoadingTasksInBackground = true;
        UI_HELPER_EXECUTOR.execute(() -> {
            if (!mResultsBg.isValidForRequest(requestLoadId, loadKeysOnly)) {
                mResultsBg = loadTasksInBackground(Integer.MAX_VALUE, requestLoadId, loadKeysOnly);
            }
            TaskLoadResult loadResult = mResultsBg;
            mMainThreadExecutor.execute(() -> {
                mLoadingTasksInBackground = false;
                mResultsUi = loadResult;
                if (callback != null) {
                    ArrayList<Task> result = copyOf(mResultsUi);
                    callback.accept(result);
                }
            });
        });
        return requestLoadId;
    }

        同样是RecentTasksList中,getTasks方法会去调用loadTasksInBackground方法去加载RecentTasks,而传入的第一个参数是int类型数据的最大值,即不做任何限制,因此可以排除掉是launcher3自身的限制。

        排除掉launcher3的嫌疑后,只能把目光投向到AMS中。针对于GO设备而言,AMS可能为了节省内存,会去对RecentTasks的个数做了限制。因此我们继续去看AMS.getRecentTasks这个方法的具体实现。

        AMS.getRecentTasks最终是调用到RecentTask.getRecentTasksImpl中:

// frameworks/base/services/core/java/com/android/server/wm/RecentTasks.java
    private ArrayList<ActivityManager.RecentTaskInfo> getRecentTasksImpl(int maxNum, int flags,
            boolean getTasksAllowed, int userId, int callingUid) {
        final boolean withExcluded = (flags & RECENT_WITH_EXCLUDED) != 0;

        if (!isUserRunning(userId, FLAG_AND_UNLOCKED)) {
            Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
            return new ArrayList<>();
        }
        loadUserRecentsLocked(userId);

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

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

            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.
                if (DEBUG_RECENTS) {
                    Slog.d(TAG_RECENTS, "Skipping, user setup not complete: " + task);
                }
                continue;
            }

            res.add(createRecentTaskInfo(task, true /* stripExtras */));
        }
        return res;
    }

        从getRecentTasksImpl的源码中推测,此方法并没有什么明显的限制,最多只是判断了Task的合法性,那么只能怀疑是mTasks中的个数可能真的是最多只有9个,因此,在此处加了个打印,在调用进来是将mTasks的元素个数值打印出来,发现确实到9后,再打开新的应用,此时该值仍是9。因此继续追查mTasks会在哪有remove的操作,将超出限制的元素清除掉。在源码中搜索mTasks.remove,排查所有调用的地方。

        下面就是可疑之处,然后加上打印,发现确实每次打印新的应用,增加新的RecentTask时,就会被触发,trimInactiveRecentTasks方法中有两处mTasks.remove调用:

// frameworks/base/services/core/java/com/android/server/wm/RecentTasks.java
    private void trimInactiveRecentTasks() {
        if (mFreezeTaskListReordering) {
            // Defer trimming inactive recent tasks until we are unfrozen
            return;
        }

        int recentsCount = mTasks.size();

        // Remove from the end of the list until we reach the max number of recents
        while (recentsCount > mGlobalMaxNumTasks) {
            final Task task = mTasks.remove(recentsCount - 1);
            notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
            recentsCount--;
            if (DEBUG_RECENTS_TRIM_TASKS) {
                Slog.d(TAG, "Trimming over max-recents task=" + task
                        + " max=" + mGlobalMaxNumTasks);
            }
        }

        // Remove any tasks that belong to currently quiet profiles
        final int[] profileUserIds = getCurrentProfileIds();
        mTmpQuietProfileUserIds.clear();
        for (int userId : profileUserIds) {
            final UserInfo userInfo = getUserInfo(userId);
            if (userInfo != null && userInfo.isManagedProfile() && userInfo.isQuietModeEnabled()) {
                mTmpQuietProfileUserIds.put(userId, true);
            }
            if (DEBUG_RECENTS_TRIM_TASKS) {
                Slog.d(TAG, "User: " + userInfo
                        + " quiet=" + mTmpQuietProfileUserIds.get(userId));
            }
        }

        // Remove any inactive tasks, calculate the latest set of visible tasks.
        int numVisibleTasks = 0;
        for (int i = 0; i < mTasks.size(); ) {
            final Task task = mTasks.get(i);

            if (isActiveRecentTask(task, mTmpQuietProfileUserIds)) {
                if (!mHasVisibleRecentTasks) {
                    // Keep all active tasks if visible recent tasks is not supported
                    i++;
                    continue;
                }

                if (!isVisibleRecentTask(task)) {
                    // Keep all active-but-invisible tasks
                    i++;
                    continue;
                } else {
                    numVisibleTasks++;
                    if (isInVisibleRange(task, i, numVisibleTasks, false /* skipExcludedCheck */)
                            || !isTrimmable(task)) {
                        // Keep visible tasks in range
                        i++;
                        continue;
                    } else {
                        // Fall through to trim visible tasks that are no longer in range and
                        // trimmable
                        if (DEBUG_RECENTS_TRIM_TASKS) {
                            Slog.d(TAG,
                                    "Trimming out-of-range visible task=" + task);
                        }
                    }
                }
            } else {
                // Fall through to trim inactive tasks
                if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming inactive task=" + task);
            }

            // Task is no longer active, trim it from the list
            mTasks.remove(task);
            notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
            notifyTaskPersisterLocked(task, false /* flush */);
        }
    }

        其中一处是由mGlobalMaxNumTasks这个值来控制,当mTasks元素个数超过这个值时,则会remove掉超出的Task。经过打印,mGlobalMaxNumTasks这个值并不是9,这个值得来源如下:

mGlobalMaxNumTasks = ActivityTaskManager.getMaxRecentTasksStatic();
// frameworks/base/core/java/android/app/ActivityTaskManager.java
    public static int getMaxRecentTasksStatic() {
        if (sMaxRecentTasks < 0) {
            return sMaxRecentTasks = ActivityManager.isLowRamDeviceStatic() ? 36 : 48;
        }
        return sMaxRecentTasks;
    }

        对于LowRam设备而言,此值为36,也就是说,GO设置最多只能有36条RecentTask记录,而非GO最多只能有48个。

        但这个值仍不是这个问题的限制值,在加上打印是,可以得知trimInactiveRecentTasks方法remove超出的Task是方法的结束地方,回到trimInactiveRecentTasks方法,可以看到,这个方法会遍历mTasks的所有元素,然后将不符合要求的Task remove掉,而这些条件中,有一个方法是最符合我们的想法的 -- isInVisibleRange:

// frameworks/base/services/core/java/com/android/server/wm/RecentTasks.java
    private boolean isInVisibleRange(Task task, int taskIndex, int numVisibleTasks,
            boolean skipExcludedCheck) {
        if (!skipExcludedCheck) {
            // Keep the most recent task 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");
                return taskIndex == 0;
            }
        }

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

        // The given task if always treated as in visible range if it is the origin of pinned task.
        if (task.mChildPipActivity != null) return true;

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

        上面的isInVisibleRange方法中,可以看到一个很显眼的变量 -- mMaxNumVisibleTasks,当mMaxNumVisibleTasks这个值大于等于0时,则表明生效,如果可见的RecentTasks个数小于mMaxNumVisibleTasks的值,则跳过mMaxNumVisibleTasks中的remove操作,但如果超出了,则会执行remove操作。

        再看mMaxNumVisibleTasks这个值的来源:

        1.RecentTasks.setParameters,通过源码的搜索及注解,可以认为此接口只为测试所用。

        2.RecentTasks.loadParametersFromResources,这个是根据不同的方案类型读取了不同的值:

// frameworks/base/services/core/java/com/android/server/wm/RecentTasks.java
    void loadParametersFromResources(Resources res) {
        if (ActivityManager.isLowRamDeviceStatic()) {
            mMinNumVisibleTasks = res.getInteger(
                    com.android.internal.R.integer.config_minNumVisibleRecentTasks_lowRam);
            mMaxNumVisibleTasks = res.getInteger(
                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks_lowRam);
        } else if (SystemProperties.getBoolean("ro.recents.grid", false)) {
            mMinNumVisibleTasks = res.getInteger(
                    com.android.internal.R.integer.config_minNumVisibleRecentTasks_grid);
            mMaxNumVisibleTasks = res.getInteger(
                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks_grid);
        } else {
            mMinNumVisibleTasks = res.getInteger(
                    com.android.internal.R.integer.config_minNumVisibleRecentTasks);
            mMaxNumVisibleTasks = res.getInteger(
                    com.android.internal.R.integer.config_maxNumVisibleRecentTasks);
        }
        final int sessionDurationHrs = res.getInteger(
                com.android.internal.R.integer.config_activeTaskDurationHours);
        mActiveTasksSessionDurationMs = (sessionDurationHrs > 0)
                ? TimeUnit.HOURS.toMillis(sessionDurationHrs)
                : -1;
    }

        可以看到LowRam设备的值来源于config_maxNumVisibleRecentTasks_lowRam这个配置。原生配置中,此值就是9:

./core/res/res/values/config.xml:2915:    <integer name="config_maxNumVisibleRecentTasks_lowRam">9</integer>

        问题澄清。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值