RecentsActivity 是最近任务管理界面,通过该activity用户可以对启动的应用进行简单的管理。其配置信息如下
frameworks/base/packages/SystemUI/AndroidManifest.xml
<activity android:name=".recents.RecentsActivity"
android:label="@string/accessibility_desc_recent_apps"
android:exported="false"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
android:screenOrientation="behind"
android:resizeableActivity="true"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:theme="@style/RecentsTheme.Wallpaper">
<intent-filter>
<action android:name="com.android.systemui.recents.TOGGLE_RECENTS" />
</intent-filter>
</activity>
可以看到其launchMode为"singleInstance"。
RecentsActivity 启动的入口在
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
public void onClick(View v) {
awakenDreams();
toggleRecentApps();
}
};
关于
RecentsActivity ,这里主要讨论3个方面,task的获取、应用缩略图的获取、task移除。
getRecentTasks
在ams中,有一个mRecentTasks对象,该对象保存了近期启动的task任务信息,RecentsActivity实际上是要获取mRecentTasks对象
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
/**
* List of intents that were used to start the most recent tasks.
*/
final RecentTasks mRecentTasks;
在上面的入口中,toggleRecentApps()最终会调用到
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) {
...
int minNumTasksToQuery = 10;
int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
int flags = ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK |
ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
ActivityManager.RECENT_IGNORE_UNAVAILABLE |
ActivityManager.RECENT_INCLUDE_PROFILES;
if (includeFrontMostExcludedTask) {
flags |= ActivityManager.RECENT_WITH_EXCLUDED;
}
List<ActivityManager.RecentTaskInfo> tasks = null;
try {
tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
} catch (Exception e) {
Log.e(TAG, "Failed to get recent tasks", e);
}
...
return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
}
这里设置了flag,不去获取HOME_STACK_ID、DOCKED_STACK_ID、PINNED_STACK_ID上的task,根据机器内存大小设置了最大的查询数
frameworks/base/core/java/android/app/ActivityManager.java
static public int getMaxRecentTasksStatic() {
if (gMaxRecentTasks < 0) {
return gMaxRecentTasks = isLowRamDeviceStatic() ? 36 : 48;
}
return gMaxRecentTasks;
}
然后调用ActivityManager的getRecentTasksForUser方法
frameworks/base/core/java/android/app/ActivityManager.java
public List<RecentTaskInfo> getRecentTasksForUser(int maxNum, int flags, int userId)
throws SecurityException {
try {
return ActivityManagerNative.getDefault().getRecentTasks(maxNum,
flags, userId).getList();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
通过binder通信最终会进入到ams
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
int userId) {
final int callingUid = Binder.getCallingUid();
userId = mUserController.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
false, ALLOW_FULL_ONLY, "getRecentTasks", null);
final boolean includeProfiles = (flags & ActivityManager.RECENT_INCLUDE_PROFILES) != 0;
final boolean withExcluded = (flags&ActivityManager.RECENT_WITH_EXCLUDED) != 0;
synchronized (this) {
final boolean allowed = isGetTasksAllowed("getRecentTasks", Binder.getCallingPid(),
callingUid);
final boolean detailed = checkCallingPermission(
android.Manifest.permission.GET_DETAILED_TASKS)
== PackageManager.PERMISSION_GRANTED;
if (!isUserRunning(userId, ActivityManager.FLAG_AND_UNLOCKED)) {
Slog.i(TAG, "user " + userId + " is still locked. Cannot load recents");
return ParceledListSlice.emptyList();
}
mRecentTasks.loadUserRecentsLocked(userId);
final int recentsCount = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res =
new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount);
final Set<Integer> includedUsers;
if (includeProfiles) {
includedUsers = mUserController.getProfileIds(userId);
} else {
includedUsers = new HashSet<>();
}
includedUsers.add(Integer.valueOf(userId));
for (int i = 0; i < recentsCount && maxNum > 0; i++) {
TaskRecord tr = mRecentTasks.get(i);
// Only add calling user or related users recent tasks
if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
continue;
}
if (tr.realActivitySuspended) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, activity suspended: " + tr);
continue;
}
// Return the entry if desired by the caller. We always return
// the first entry, because callers always expect this to be the
// foreground app. We may filter others if the caller has
// not supplied RECENT_WITH_EXCLUDED and there is some reason
// we should exclude the entry.
if (i == 0
|| withExcluded
|| (tr.intent == null)
|| ((tr.intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
== 0)) {
if (!allowed) {
// 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 (!tr.isHomeTask() && tr.effectiveUid != callingUid) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
continue;
}
}
if ((flags & ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS) != 0) {
if (tr.stack != null && tr.stack.isHomeStack()) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
"Skipping, home stack task: " + tr);
continue;
}
}
if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK) != 0) {
final ActivityStack stack = tr.stack;
if (stack != null && stack.isDockedStack() && stack.topTask() == tr) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
"Skipping, top task in docked stack: " + tr);
continue;
}
}
if ((flags & ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS) != 0) {
if (tr.stack != null && tr.stack.isPinnedStack()) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
"Skipping, pinned stack task: " + tr);
continue;
}
}
if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
// Don't include auto remove tasks that are finished or finishing.
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
"Skipping, auto-remove without activity: " + tr);
continue;
}
if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0
&& !tr.isAvailabl