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.isAvailable) {
-
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-
"Skipping, unavail real act: " + tr);
-
continue;
-
}
-
if (!tr.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: " + tr);
-
continue;
-
}
-
ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
-
if (!detailed) {
-
rti.baseIntent.replaceExtras((Bundle)null);
-
}
-
res.add(rti);
-
maxNum--;
-
}
-
}
-
return new ParceledListSlice<>(res);
-
}
-
}
首先loadUserRecentsLocked
-
frameworks/base/services/core/java/com/android/server/am/RecentTasks.java
-
void loadUserRecentsLocked(int userId) {
-
if (!mUsersWithRecentsLoaded.get(userId)) {
-
// Load the task ids if not loaded.
-
loadPersistedTaskIdsForUserLocked(userId);
-
Slog.i(TAG, "Loading recents for user " + userId + " into memory.");
-
addAll(mTaskPersister.restoreTasksForUserLocked(userId));
-
cleanupLocked(userId);
-
mUsersWithRecentsLoaded.put(userId, true);
-
}
-
}
通过restoreTasksForUserLocked方法去获取task列表
-
frameworks/base/services/core/java/com/android/server/am/TaskPersister.java
-
List<TaskRecord> restoreTasksForUserLocked(final int userId) {
-
final ArrayList<TaskRecord> tasks = new ArrayList<TaskRecord>();
-
ArraySet<Integer> recoveredTaskIds = new ArraySet<Integer>();
-
File userTasksDir = getUserTasksDir(userId);
-
File[] recentFiles = userTasksDir.listFiles();
-
if (recentFiles == null) {
-
Slog.e(TAG, "restoreTasksForUserLocked: Unable to list files from " + userTasksDir);
-
return tasks;
-
}
-
for (int taskNdx = 0; taskNdx < recentFiles.length; ++taskNdx) {
-
File taskFile = recentFiles[taskNdx];
-
if (DEBUG) {
-
Slog.d(TAG, "restoreTasksForUserLocked: userId=" + userId
-
+ ", taskFile=" + taskFile.getName());
-
}
-
BufferedReader reader = null;
-
boolean deleteFile = false;
-
try {
-
reader = new BufferedReader(new FileReader(taskFile));
-
final XmlPullParser in = Xml.newPullParser();
-
in.setInput(reader);
-
int event;
-
while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
-
event != XmlPullParser.END_TAG) {
-
final String name = in.getName();
-
if (event == XmlPullParser.START_TAG) {
-
if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: START_TAG name=" + name);
-
if (TAG_TASK.equals(name)) {
-
final TaskRecord task = TaskRecord.restoreFromXml(in, mStackSupervisor);
-
if (DEBUG) Slog.d(TAG, "restoreTasksForUserLocked: restored task="
-
+ task);
-
if (task != null) {
-
// XXX Don't add to write queue... there is no reason to write
-
// out the stuff we just read, if we don't write it we will
-
// read the same thing again.
-
// mWriteQueue.add(new TaskWriteQueueItem(task));
-
final int taskId = task.taskId;
-
if (mStackSupervisor.anyTaskForIdLocked(taskId,
-
/* restoreFromRecents= */ false, 0) != null) {
-
// Should not happen.
-
Slog.wtf(TAG, "Existing task with taskId " + taskId + "found");
-
} else if (userId != task.userId) {
-
// Should not happen.
-
Slog.wtf(TAG, "Task with userId " + task.userId + " found in "
-
+ userTasksDir.getAbsolutePath());
-
} else {
-
// Looks fine.
-
mStackSupervisor.setNextTaskIdForUserLocked(taskId, userId);
-
task.isPersistable = true;
-
tasks.add(task);
-
recoveredTaskIds.add(taskId);
-
}
-
} else {
-
Slog.e(TAG, "restoreTasksForUserLocked: Unable to restore taskFile="
-
+ taskFile + ": " + fileToString(taskFile));
-
}
-
} else {
-
Slog.wtf(TAG, "restoreTasksForUserLocked: Unknown xml event=" + event
-
+ " name=" + name);
-
}
-
}
-
XmlUtils.skipCurrentTag(in);
-
}
-
} catch (Exception e) {
-
Slog.wtf(TAG, "Unable to parse " + taskFile + ". Error ", e);
-
Slog.e(TAG, "Failing file: " + fileToString(taskFile));
-
deleteFile = true;
-
} finally {
-
IoUtils.closeQuietly(reader);
-
if (deleteFile) {
-
if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
-
taskFile.delete();
-
}
-
}
-
}
-
...
-
return tasks;
-
}
主要是去解析xml文件,位于/data/system_ce/0/recent_tasks目录下
可以看到这些task信息是以文件形式保存的,这就是重启后进RecentsActivity看到task还在的原因。
解析完这些xml文件就获取到了初始的task信息。
但实际上loadUserRecentsLocked里面的语句是只会在开机初始化时执行一次,因此这里相当于什么都没有做。
然后还要回到
-
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
-
public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
-
int userId) {
-
...
-
synchronized (this) {
-
...
-
mRecentTasks.loadUserRecentsLocked(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.isAvailable) {
-
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
-
"Skipping, unavail real act: " + tr);
-
continue;
-
}
-
if (!tr.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: " + tr);
-
continue;
-
}
-
ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
-
if (!detailed) {
-
rti.baseIntent.replaceExtras((Bundle)null);
-
}
-
res.add(rti);
-
maxNum--;
-
}
-
}
-
return new ParceledListSlice<>(res);
-
}
-
}
根据之前设置的flag去排除掉不需要的task,最后调用createRecentTaskInfoFromTaskRecord()方法把TaskRecord信息转换成RecentTaskInfo的信息
-
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
-
private ActivityManager.RecentTaskInfo createRecentTaskInfoFromTaskRecord(TaskRecord tr) {
-
// Update the task description to reflect any changes in the task stack
-
tr.updateTaskDescription();
-
// Compose the recent task info
-
ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
-
rti.id = tr.getTopActivity() == null ? INVALID_TASK_ID : tr.taskId;
-
rti.persistentId = tr.taskId;
-
rti.baseIntent = new Intent(tr.getBaseIntent());
-
rti.origActivity = tr.origActivity;
-
rti.realActivity = tr.realActivity;
-
rti.description = tr.lastDescription;
-
rti.stackId = tr.stack != null ? tr.stack.mStackId : -1;
-
rti.userId = tr.userId;
-
rti.taskDescription = new ActivityManager.TaskDescription(tr.lastTaskDescription);
-
rti.firstActiveTime = tr.firstActiveTime;
-
rti.lastActiveTime = tr.lastActiveTime;
-
rti.affiliatedTaskId = tr.mAffiliatedTaskId;
-
rti.affiliatedTaskColor = tr.mAffiliatedTaskColor;
-
rti.numActivities = 0;
-
if (tr.mBounds != null) {
-
rti.bounds = new Rect(tr.mBounds);
-
}
-
rti.isDockable = tr.canGoInDockedStack();
-
rti.resizeMode = tr.mResizeMode;
-
ActivityRecord base = null;
-
ActivityRecord top = null;
-
ActivityRecord tmp;
-
for (int i = tr.mActivities.size() - 1; i >= 0; --i) {
-
tmp = tr.mActivities.get(i);
-
if (tmp.finishing) {
-
continue;
-
}
-
base = tmp;
-
if (top == null || (top.state == ActivityState.INITIALIZING)) {
-
top = base;
-
}
-
rti.numActivities++;
-
}
-
rti.baseActivity = (base != null) ? base.intent.getComponent() : null;
-
rti.topActivity = (top != null) ? top.intent.getComponent() : null;
-
return rti;
-
}
从这里可以看到recenttask是以TaskReord为单位进行管理的,不是一app或者activity为单位进行管理。
到这里就获取了所需的RecentTaskInfo信息。
从这里可以看到mRecentTasks只是剔除了一些不符合要求的task做了些简单的处理就返回了,那么mRecentTasks真正是在哪里赋值的呢?
有两个地方,一是在startActivity的过程中,二是在activity 重新resume时。
先看startActivity时,这里省略了startActivity的具体过程
-
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
-
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
-
boolean andResume, boolean checkConfig) throws RemoteException {
-
...
-
if (andResume) {
-
// As part of the process of launching, ActivityThread also performs
-
// a resume.
-
stack.minimalResumeActivityLocked(r);
-
}
-
...
-
return true;
-
}
在minimalResumeActivityLocked方法中将TaskRecord对象加入到mRecentTasks对象中
-
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
-
void minimalResumeActivityLocked(ActivityRecord r) {
-
...
-
mRecentTasks.addLocked(r.task);
-
...
-
}
这里有个疑问,ActivityStack中的mRecentTasks是怎么和ActivityManagerService的mRecentTasks联系起来的呢?
在ams初始化时创建了ActivityStackSupervisor对象,用来辅助ams管理ActivityStack
-
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
-
public ActivityManagerService(Context systemContext) {
-
...
-
mStackSupervisor = new ActivityStackSupervisor(this);
-
mActivityStarter = new ActivityStarter(this, mStackSupervisor);
-
mRecentTasks = new RecentTasks(this, mStackSupervisor);
-
...
-
}
同时也创建了mRecentTasks对象,参数是ams和mStackSupervisor。
RecentTasks的构造函数中又把自己通过setRecentTasks的方法保存到了ActivityStackSupervisor
-
frameworks/base/services/core/java/com/android/server/am/RecentTasks.java
-
RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) {
-
...
-
mStackSupervisor.setRecentTasks(this);
-
}
-
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
-
...
-
private RecentTasks mRecentTasks;
-
...
-
void setRecentTasks(RecentTasks recentTasks) {
-
mRecentTasks = recentTasks;
-
}
ActivityStackSupervisor中也保存了一个RecentTasks对象,这样实际上和ActivityManagerService的mRecentTasks指向了同一个地址。
在ActivityStackSupervisor中创建ActivityStack时,又把mRecentTasks传递到了ActivityStack中,
-
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
-
ActivityContainer(int stackId) {
-
synchronized (mService) {
-
...
-
mStack = new ActivityStack(this, mRecentTasks);
-
...
-
}
-
}
这样ActivityStack中的mRecentTasks就和ActivityManagerService的mRecentTasks保持一致了。
现在回到mRecentTasks的赋值问题,第二个时机是在activity 重新resume时。
-
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
-
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
-
...
-
mRecentTasks.addLocked(next.task);
-
...
-
}
getTaskThumbnail
获取到RecentTaskInfo之后就要去获取缩略图信息了
-
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
-
public void getThumbnail(int taskId, ThumbnailData thumbnailDataOut) {
-
...
-
ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
-
...
-
}
很简洁,就调用了ActivityManager的方法
-
frameworks/base/core/java/android/app/ActivityManager.java
-
public TaskThumbnail getTaskThumbnail(int id) throws SecurityException {
-
try {
-
return ActivityManagerNative.getDefault().getTaskThumbnail(id);
-
} catch (RemoteException e) {
-
throw e.rethrowFromSystemServer();
-
}
-
}
通过binder通信调用了ams的方法
-
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
-
public ActivityManager.TaskThumbnail getTaskThumbnail(int id) {
-
synchronized (this) {
-
enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER,
-
"getTaskThumbnail()");
-
final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
-
id, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
-
if (tr != null) {
-
return tr.getTaskThumbnailLocked();
-
}
-
}
-
return null;
-
}
首先根据taskid去找到TaskRecord,然后获取Thumbnail
-
public TaskThumbnail getTaskThumbnailLocked() {
-
if (stack != null) {
-
final ActivityRecord resumedActivity = stack.mResumedActivity;
-
if (resumedActivity != null && resumedActivity.task == this) {
-
final Bitmap thumbnail = stack.screenshotActivitiesLocked(resumedActivity);
-
setLastThumbnailLocked(thumbnail);
-
}
-
}
-
final TaskThumbnail taskThumbnail = new TaskThumbnail();
-
getLastThumbnail(taskThumbnail);
-
return taskThumbnail;
-
}
这里除了栈顶正在显示的TaskRecord回去实时的截取屏幕图像,其他的走getLastThumbnail
-
frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
-
void getLastThumbnail(TaskThumbnail thumbs) {
-
thumbs.mainThumbnail = mLastThumbnail;
-
thumbs.thumbnailInfo = mLastThumbnailInfo;
-
thumbs.thumbnailFileDescriptor = null;
-
if (mLastThumbnail == null) {
-
thumbs.mainThumbnail = mService.mRecentTasks.getImageFromWriteQueue(
-
mLastThumbnailFile.getAbsolutePath());
-
}
-
// Only load the thumbnail file if we don't have a thumbnail
-
if (thumbs.mainThumbnail == null && mLastThumbnailFile.exists()) {
-
try {
-
thumbs.thumbnailFileDescriptor = ParcelFileDescriptor.open(mLastThumbnailFile,
-
ParcelFileDescriptor.MODE_READ_ONLY);
-
} catch (IOException e) {
-
}
-
}
-
}
这里实际是去读取文件,这些缩略图已经保存在/data/system_ce/0/recent_images文件夹下
把这些文件读取出来就获取到了task的缩略图。
但是这里并没有看到去截屏,这些图片是什么时候截取的呢?
截图的时机是在activity onpause之后。
-
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
-
private void completePauseLocked(boolean resumeNext) {
-
...
-
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
-
}
-
frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
-
void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-
boolean preserveWindows) {
-
// First the front stacks. In case any are not fullscreen and are in front of home.
-
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
-
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
-
final int topStackNdx = stacks.size() - 1;
-
for (int stackNdx = topStackNdx; stackNdx >= 0; --stackNdx) {
-
final ActivityStack stack = stacks.get(stackNdx);
-
stack.ensureActivitiesVisibleLocked(starting, configChanges, preserveWindows);
-
}
-
}
-
}
-
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
-
final void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
-
boolean preserveWindows) {
-
...
-
makeInvisible(r, visibleBehind);
-
...
-
}
-
frameworks/base/services/core/java/com/android/server/am/ActivityStack.java
-
private void makeInvisible(ActivityRecord r, ActivityRecord visibleBehind) {
-
...
-
try {
-
setVisible(r, false);
-
...
-
} catch (Exception e) {
-
...
-
}
-
}
makeInvisible又调用了setVisible
-
private void setVisible(ActivityRecord r, boolean visible) {
-
r.visible = visible;
-
if (!visible && r.mUpdateTaskThumbnailWhenHidden) {
-
r.updateThumbnailLocked(r.task.stack.screenshotActivitiesLocked(r), null);
-
r.mUpdateTaskThumbnailWhenHidden = false;
-
}
-
...
-
}
就是在这里更新TaskThumbnail的,screenshotActivitiesLocked(r)是真正截图的地方
截图完成之后,最终通过saveImage方法保存到文件中,这里path为/data/system_ce/0/recent_images文件夹
-
frameworks/base/services/core/java/com/android/server/am/RecentTasks.java
-
void saveImage(Bitmap image, String path) {
-
mTaskPersister.saveImage(image, path);
-
}
这样需要获取缩略图时之前去读文件就可以了。
removeTask
当从RecentsActivity移除某一个应用时,实际会调用到removeTask
-
frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
-
public void removeTask(final int taskId) {
-
...
-
// Remove the task.
-
BackgroundThread.getHandler().post(new Runnable() {
-
@Override
-
public void run() {
-
mAm.removeTask(taskId);
-
}
-
});
-
}
然后调用到ams的removeTask方法,参数taskId为要移除的TaskRecord的id
-
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
-
public boolean removeTask(int taskId) {
-
enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, "removeTask()");
-
synchronized (this) {
-
final long ident = Binder.clearCallingIdentity();
-
try {
-
return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
-
} finally {
-
Binder.restoreCallingIdentity(ident);
-
}
-
}
-
}
调用该方法首先会检查是否有声明android.Manifest.permission.REMOVE_TASKS的权限,这里我们是有声明这个权限的。然后调用removeTaskByIdLocked
-
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
-
private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
-
boolean removeFromRecents) {
-
final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
-
taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
-
if (tr != null) {
-
tr.removeTaskActivitiesLocked();
-
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
-
if (tr.isPersistable) {
-
notifyTaskPersisterLocked(null, true);
-
}
-
return true;
-
}
-
Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
-
return false;
-
}
先看下这里的参数,taskid为要移除的task id,killProcess为true,表示要杀掉该taskrecord里所有activity所在的进程,removeFromRecents为true,表示要从mRecentTasks移除。
这里通过taskid找到该TaskRecord,然后首先调用了TaskRecord的removeTaskActivitiesLocked方法
-
frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
-
public void removeTaskActivitiesLocked() {
-
// Just remove the entire task.
-
performClearTaskAtIndexLocked(0);
-
}
-
frameworks/base/services/core/java/com/android/server/am/TaskRecord.java
-
final void performClearTaskAtIndexLocked(int activityNdx) {
-
int numActivities = mActivities.size();
-
for ( ; activityNdx < numActivities; ++activityNdx) {
-
final ActivityRecord r = mActivities.get(activityNdx);
-
if (r.finishing) {
-
continue;
-
}
-
if (stack == null) {
-
// Task was restored from persistent storage.
-
r.takeFromHistory();
-
mActivities.remove(activityNdx);
-
--activityNdx;
-
--numActivities;
-
} else if (stack.finishActivityLocked(
-
r, Activity.RESULT_CANCELED, null, "clear-task-index", false)) {
-
--activityNdx;
-
--numActivities;
-
}
-
}
-
}
传递下来的参数activityNdx = 0,表示吧整个TaskRecord移除。这里的mActivities是该TaskRecord中保存的所有ActivityRecord对象,stack是该TaskRecord所在的ActivityStack,这里不为空,所以会调用ActivityStack的finishActivityLocked方法去移除该TaskRecord中的ActivityRecord对象。
从ActivityStack移除完TaskRecord之后回到removeTaskByIdLocked,然后又调用了cleanUpRemovedTaskLocked方法
-
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
-
private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
-
boolean removeFromRecents) {
-
if (removeFromRecents) {
-
mRecentTasks.remove(tr);
-
tr.removedFromRecents();
-
}
-
ComponentName component = tr.getBaseIntent().getComponent();
-
if (component == null) {
-
Slog.w(TAG, "No component for base intent of task: " + tr);
-
return;
-
}
-
// Find any running services associated with this app and stop if needed.
-
mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
-
if (!killProcess) {
-
return;
-
}
-
// Determine if the process(es) for this task should be killed.
-
final String pkg = component.getPackageName();
-
ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
-
ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
-
for (int i = 0; i < pmap.size(); i++) {
-
SparseArray<ProcessRecord> uids = pmap.valueAt(i);
-
for (int j = 0; j < uids.size(); j++) {
-
ProcessRecord proc = uids.valueAt(j);
-
if (proc.userId != tr.userId) {
-
// Don't kill process for a different user.
-
continue;
-
}
-
if (proc == mHomeProcess) {
-
// Don't kill the home process along with tasks from the same package.
-
continue;
-
}
-
if (!proc.pkgList.containsKey(pkg)) {
-
// Don't kill process that is not associated with this task.
-
continue;
-
}
-
for (int k = 0; k < proc.activities.size(); k++) {
-
TaskRecord otherTask = proc.activities.get(k).task;
-
if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
-
// Don't kill process(es) that has an activity in a different task that is
-
// also in recents.
-
return;
-
}
-
}
-
if (proc.foregroundServices) {
-
// Don't kill process(es) with foreground service.
-
return;
-
}
-
// Add process to kill list.
-
procsToKill.add(proc);
-
}
-
}
-
// Kill the running processes.
-
for (int i = 0; i < procsToKill.size(); i++) {
-
ProcessRecord pr = procsToKill.get(i);
-
if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
-
&& pr.curReceiver == null) {
-
pr.kill("remove task", true);
-
} else {
-
// We delay killing processes that are not in the background or running a receiver.
-
pr.waitingToKill = "remove task";
-
}
-
}
-
}
这里removeFromRecents为true,因此会先将该TaskRecord从mRecentTasks中移除,然后过滤出可以杀掉的Process的ProcessRecord信息,放到procsToKill列表中。分两种情况,如果是SCHED_GROUP_BACKGROUND类型并且没有在执行reciver,会调用kill方法立即去杀掉。否则简单将procsToKill中ProcessRecord的waitingToKill字段设置为remove task。这里涉及到Android进程管理的相关知识,可参看