Android 近期任务列表Recents功能
前段时间在做多任务管理的功能,即Android系统Recents功能,该功能的上层实现是在SystemUI里面,通过阅读SystemUI及framework相关的源码,当前系统版本是Android 8.1,现将学习记录如下:
一.功能概览
image.pngRecents功能是在SystemUI应用里面实现的,SystemUI作为Android系统核心应用,与Framework有密切关系,当然Recents功能也是完全依赖Framework的;
二.重要功能类
1.SystemServicesProxy.java
与系统服务级的交互代理类,SystemUI应用依靠该类与AMS进行交互;
1.1.获取最近任务列表
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) {
.........
.........
try {
// 最终会调用到AMS
tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
} catch (Exception e) {
Log.e(TAG, "Failed to get recent tasks", e);
}
Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
while (iter.hasNext()) {
ActivityManager.RecentTaskInfo t = iter.next();
// Remove the task if it or it's package are blacklsited
// 可以将不想显示的应用加入黑名单
if (sRecentsBlacklist.contains(t.realActivity.getClassName()) ||
sRecentsBlacklist.contains(t.realActivity.getPackageName())) {
iter.remove();
continue;
}
// Remove the task if it is marked as excluded, unless it is the first most task and we
// are requested to include it
//删除在AndroidManifest.xml包含android:excludeFromRecents="true"的任务
boolean isExcluded = (t.baseIntent.getFlags() Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
== Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
isExcluded |= quietProfileIds.contains(t.userId);
if (isExcluded && (!isFirstValidTask || !includeFrontMostExcludedTask)) {
iter.remove();
}
}
return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
}
1.2.删除最近task
public void removeTask(final int taskId) {
// Remove the task.
mUiOffloadThread.submit(() -> {
mAm.removeTask(taskId);
});
}
1.3.监听task stack变化,更新最近任务列表
private android.app.TaskStackListener mTaskStackListener = new android.app.TaskStackListener() {
@Override
public void onTaskStackChanged() throws RemoteException {
}
......
......
@Override
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
throws RemoteException {
}
}
2.Recents.java
继承SystemUI抽象类,recents task功能的入口,初始化必要的实例,提供触发接口;
@Override
public void start() {
//初始化实例
sSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
sTaskLoader = new RecentsTaskLoader(mContext);
mImpl = new RecentsImpl(mContext);
}
//rencets入口
@Override
public void toggleRecentApps() {
......
......
mImpl.toggleRecents(growTarget);
}
3.RecentsImpl.java
Recents主要功能逻辑执行类,包括监听task变化及触发Recents界面;
3.1.监听TaskStack变化来加载任务列表
class TaskStackListenerImpl extends TaskStackListener {
@Override
public void onTaskStackChangedBackground() {
// Load the next task only if we aren't svelte
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo runningTaskInfo = ssp.getRunningTask();
RecentsTaskLoader loader = Recents.getTaskLoader();
RecentsTaskLoadPlan plan = loader.createLoadPlan(mContext);
loader.preloadTasks(plan, -1, false /* includeFrontMostExcludedTask */);
TaskStack stack = plan.getTaskStack();
RecentsActivityLaunchState launchState = new RecentsActivityLaunchState();
RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
......
......
loader.loadTasks(mContext, plan, launchOpts);
}
3.2.启动Recents Activity
public void toggleRecents(int growTarget) {
......
startRecentsActivity(runningTask, isHomeStackVisible.value, true /* animate */,growTarget);
......
}
4.RecentsTaskLoader.java
Recents最近任务加载入口类
4.1.创建RecentsTaskLoadPlan
public RecentsTaskLoadPlan createLoadPlan(Context context) {
RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(context);
return plan;
}
4.2.启动plan加载任务列表
public synchronized void loadTasks(Context context, RecentsTaskLoadPlan plan,
RecentsTaskLoadPlan.Options opts) {
......
plan.executePlan(opts, this);
......
}
5.RecentsTaskLoadPlan.java
Recents最近任务加载执行类
void preloadRawTasks(boolean includeFrontMostExcludedTask) {
//调用SystemServicesProxy来获取最近任务列表
SystemServicesProxy ssp = Recents.getSystemServices();
mRawTasks = ssp.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(),
currentUserId, includeFrontMostExcludedTask, mCurrentQuietProfiles);
// Since the raw tasks are given in most-recent to least-recent order, we need to reverse it
Collections.reverse(mRawTasks);
}
通过SystemServicesPeroxy来获取最近任务信息列表mRawTasks,存储的是ActivityManager.RecentTaskInfo,然后进行反序排列;
preloadPlan()用来预加载最近任务列表;
void preloadPlan(RecentsTaskLoader loader, int runningTaskId,
boolean includeFrontMostExcludedTask) {
......
......
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, t.stackId, t.baseIntent,
t.userId, t.firstActiveTime, t.lastActiveTime);
ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
String titleDescription = loader.getAndUpdateContentDescription(taskKey,
t.taskDescription, res);
.......
Drawable icon = isStackTask
? loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, res, false)
: null;
ThumbnailData thumbnail = loader.getAndUpdateThumbnail(taskKey,
false /* loadIfNotCached */, false /* storeInCache */);
......
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
thumbnail, title, titleDescription, dismissDescription, appInfoDescription,
activityColor, backgroundColor, isLaunchTarget, isStackTask, isSystemApp,
t.supportsSplitScreenMultiWindow, t.bounds, t.taskDescription, t.resizeMode,
t.topActivity,
isLocked);
allTasks.add(task);
mStack = new TaskStack();
mStack.setTasks(mContext, allTasks, false /* notifyStackChanges */);
}
该方法主要执行了以下主要逻辑:
1.遍历mRawTasks,将ActivityManager.RecentTaskInfo封装成Task;
2.将Task加入allTasks中进行管理;
3.创建TaskStack实例,进行选择处理从而得到最终任务列表,后续获取最近任务也是通过TaskStack.getStackTasks()来获取;
executePlan()用来真正加载任务对应的icon和thumbnail;
void executePlan(Options opts, RecentsTaskLoader loader) {
Resources res = mContext.getResources();
// Iterate through each of the tasks and load them according to the load conditions.
ArrayList<Task> tasks = mStack.getStackTasks();
int taskCount = tasks.size();
for (int i = 0; i < taskCount; i++) {
Task task = tasks.get(i);
Task.TaskKey taskKey = task.key;
boolean isRunningTask = (task.key.id == opts.runningTaskId);
boolean isVisibleTask = i >= (taskCount - opts.numVisibleTasks);
boolean isVisibleThumbnail = i >= (taskCount - opts.numVisibleTaskThumbnails);
// If requested, skip the running task
if (opts.onlyLoadPausedActivities && isRunningTask) {
continue;
}
if (opts.loadIcons && (isRunningTask || isVisibleTask)) {
if (task.icon == null) {
task.icon = loader.getAndUpdateActivityIcon(taskKey, task.taskDescription, res,true);
}
}
if (opts.loadThumbnails && isVisibleThumbnail) {
task.thumbnail = loader.getAndUpdateThumbnail(taskKey,true /* loadIfNotCached */, true /* storeInCache */);
}
}
}
该方法里面相当于执行了懒加载,根据参数判断来是否真正加载到内存,主要为了降低资源消耗;
三.启动
1.启动流程
在PhoneWindowManager里面监听按键处理,通过调用最终会调用到Recents里面的toggleRecentApps()方法,从而触发Recents最近任务列表界面,本文只画出调用流程图,代码就不贴了,详情可根据流程图参考源码来进一步了解。
流程如下:
从上面流程图可以看到,PhoneWindowManager是依靠StatusBarManagerService来进行分发处理的。
2.注册流程
注册过程重点看一下SystemUIApplication这个类:
public class SystemUIApplication extends Application implements SysUiServiceProvider {
......
private final Class<?>[] SERVICES = new Class[] {
.....
CommandQueue.CommandQueueStart.class,
......
Recents.class,
.....
SystemBars.class,
......
};
private final Map<Class<?>, Object> mComponents = new HashMap<>();
private void startServicesIfNeeded(Class<?>[] services) {
......
final int N = services.length;
for (int i = 0; i < N; i++) {
Class<?> cl = services[i];
.....
try {
Object newService = SystemUIFactory.getInstance().createInstance(cl);
mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
} ......
mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
.......
mServices[i].start();
.......
if (mBootCompleted) {
mServices[i].onBootCompleted();
}
}
......
mServicesStarted = true;
}
}
上述类就是启动各个SystemUI子类的入口,Recents相关启动的类为:
CommandQueue.CommandQueueStart.class:创建了CommandQueue实例,然后执行了putComponent()供其他类使用CommandQueue实例,通过该实例调用toggleRecents();
Recents.class:将自己注册到CommandQueue,来执行toggleRecents();
SystemBars.class:启动StatusBar,在StatusBar内部将CommandQueue实例注册到StatusBarManagerService中去,在StatusBarManagerService中调用CommandQueue的方法;
通过反射来创建以上相关类的对象,创建完后先进行赋值操作,将每个service中的mComponents赋值为同一个,这样任何一个类对象putComponent(),其它类对象都可以通过getComponent()获取到,然后执行start()在内部执行对应的创建及注册操作。
注册过程如下:
总结一下
系统system_server进程启动后,会启动SystemUIService,接着就进行一系列调用,在StatusBar的start()方法内将CommandQueue对象通过调用StatusBarManagerService的registerStatusBar()注册到StatusBarManagerService中,后续收到touch事件进入Recents就通过CommandQueue来进行处理了。
四.显示流程
调用toggleRecentApps(),会启动RecentsActivity,在RecentsActivity内部会加载RecentsView,在RecentsView里面会加载TaskStackView,在TaskStackView里面循环加载TaskView,最终显示task的详细内容,本文只画出调用流程图,代码就不贴了,详情可根据流程图参考源码来进一步了解:
image.png
五.Task及Snapshot获取
1.Task列表获取流程
Task列表获取入口是:List<ActivityManager.RecentTaskInfo> mRawTask =
ssp.getRecentTasks(xx,xx,xx,xx);从SystemServicesProxy.java里面的逻辑调用开始,调用流程如下:
public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks,
int userId,boolean includeFrontMostExcludedTask,
ArraySet<Integer> quietProfileIds) {
....................
List<ActivityManager.RecentTaskInfo> tasks = null;
try {
tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
}
Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
while (iter.hasNext()) {
// 执行过滤
}
//返回最终的任务列表
return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
}
调用到AMS中,从RecentTasks中获取到TaskRecord列表,然后封装成RecentTaskInfo列表;
public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(
int maxNum, int flags, int userId) {
...............
mRecentTasks.loadUserRecentsLocked(userId);
final int recentsCount = mRecentTasks.size();
ArrayList<ActivityManager.RecentTaskInfo> res =
new ArrayList<>(maxNum < recentsCount ? maxNum : recentsCount);
for (int i = 0; i < recentsCount && maxNum > 0; i++) {
TaskRecord tr = mRecentTasks.get(i);
....................
//将TaskRecord转换为RecentTaskInfo
ActivityManager.RecentTaskInfo rti =
createRecentTaskInfoFromTaskRecord(tr);
res.add(rti);
}
//返回RecentTaskInfo列表
return new ParceledListSlice<>(res);
}
终通过RecentTasks获取到TaskRecord列表,接下来的调用流程后面会讲到;
2.Snapshot获取流程
缩略图获取入口是:ThumbnailData thumbnailData = ssp.getTaskThumbnail(taskKey.id,true /* reducedResolution */);从SystemServicesProxy.java里面的逻辑调用开始,调用流程如下:
public @NonNull ThumbnailData getThumbnail(int taskId, boolean reducedResolution) {
........
//ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", true);默认为true,进入
if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
ActivityManager.TaskSnapshot snapshot = null;
try {
//调用到ActivityManagerService里面的方法
snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
} catch (RemoteException e) {
Log.w(TAG, "Failed to retrieve snapshot", e);
}
if (snapshot != null) {
thumbnailData = ThumbnailData.createFromTaskSnapshot(snapshot);
} else {
return new ThumbnailData();
}
} else {
.............
}
return thumbnailData;
}
从入口SystemServicesProxy会调用到AMS中的方法;
//2.ActivityManagerService.java
public TaskSnapshot getTaskSnapshot(int taskId, boolean reducedResolution) {
enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
final long ident = Binder.clearCallingIdentity();
try {
final TaskRecord task;
synchronized (this) {
task = mStackSupervisor.anyTaskForIdLocked(taskId,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS, INVALID_STACK_ID);
if (task == null) {
Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
return null;
}
}
// Don't call this while holding the lock as this operation might hit the disk.
return task.getSnapshot(reducedResolution);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
//3.TaskRecord.java
TaskSnapshot getSnapshot(boolean reducedResolution) {
// TODO: Move this to {@link TaskWindowContainerController} once recent tasks are more
// synchronized between AM and WM.
return mService.mWindowManager.getTaskSnapshot(taskId, userId, reducedResolution);
}
//4.WindowManagerService.java
public TaskSnapshot getTaskSnapshot(int taskId, int userId, boolean reducedResolution) {
return mTaskSnapshotController.getSnapshot(taskId, userId, true /* restoreFromDisk */,
reducedResolution);
}
经过上面一步一步的调用,最终会调用到TaskSnapshotController这个类中,由这个类来统一管理执行task的截图处理逻辑;
//5.TaskSnapshotController.java
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
boolean reducedResolution) {
//最终从TaskSnapshotCache里面获取[前面已经存储了]
return mCache.getSnapshot(taskId, userId, restoreFromDisk, reducedResolution
|| DISABLE_FULL_SIZED_BITMAPS);
}
//6.TaskSnapshotCache.java
//截完图后会调用该方法存储更新
void putSnapshot(Task task, TaskSnapshot snapshot) {
final CacheEntry entry = mRunningCache.get(task.mTaskId);
if (entry != null) {
mAppTaskMap.remove(entry.topApp);
}
final AppWindowToken top = task.getTopChild();
mAppTaskMap.put(top, task.mTaskId);
mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, task.getTopChild()));
}
//先从Cache里面取,没有的话再从disk里面取
@Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
boolean reducedResolution) {
synchronized (mService.mWindowMap) {
// Try the running cache.
final CacheEntry entry = mRunningCache.get(taskId);
if (entry != null) {
return entry.snapshot;
}
}
// Try to restore from disk if asked.
if (!restoreFromDisk) {
return null;
}
return tryRestoreFromDisk(taskId, userId, reducedResolution);
}
//通过TaskSnapshotLoader从disk里面获取
private TaskSnapshot tryRestoreFromDisk(int taskId, int userId, boolean reducedResolution) {
final TaskSnapshot snapshot = mLoader.loadTask(taskId, userId, reducedResolution);
if (snapshot == null) {
return null;
}
return snapshot;
}
//7.TaskSnapshotLoader.java
TaskSnapshot loadTask(int taskId, int userId, boolean reducedResolution) {
final File protoFile = mPersister.getProtoFile(taskId, userId);
final File bitmapFile = reducedResolution
? mPersister.getReducedResolutionBitmapFile(taskId, userId)
: mPersister.getBitmapFile(taskId, userId);
if (bitmapFile == null || !protoFile.exists() || !bitmapFile.exists()) {
return null;
}
try {
final byte[] bytes = Files.readAllBytes(protoFile.toPath());
final TaskSnapshotProto proto = TaskSnapshotProto.parseFrom(bytes);
final Options options = new Options();
options.inPreferredConfig = Config.HARDWARE;
final Bitmap bitmap = BitmapFactory.decodeFile(bitmapFile.getPath(), options);
if (bitmap == null) {
Slog.w(TAG, "Failed to load bitmap: " + bitmapFile.getPath());
return null;
}
final GraphicBuffer buffer = bitmap.createGraphicBufferHandle();
if (buffer == null) {
Slog.w(TAG, "Failed to retrieve gralloc buffer for bitmap: "
+ bitmapFile.getPath());
return null;
}
return new TaskSnapshot(buffer, proto.orientation,
new Rect(proto.insetLeft, proto.insetTop, proto.insetRight, proto.insetBottom),
reducedResolution, reducedResolution ? REDUCED_SCALE : 1f);
} catch (IOException e) {
Slog.w(TAG, "Unable to load task snapshot data for taskId=" + taskId);
return null;
}
}
//8.TaskSnapshotPersister.java
1.将snapshot存入/data/system_ce/0/snapshots/
2.从/data/system_ce/0/snapshots/里面取snapshot
3.流程总结
recents.png六.TaskRecord存储
1.存储时机
在启动一个Activity后,在Activity Resume后通过mRecentTasks.addLocked(task)将Activity对应的TaskRecord进行存储,具体代码入口为:
void setResumedActivityLocked(ActivityRecord r, String reason) {
mResumedActivity = r;
r.state = ActivityState.RESUMED;
mService.setResumedActivityUncheckLocked(r, reason);
final TaskRecord task = r.getTask();
task.touchActiveTime();
//将task加入RecentTasks
mRecentTasks.addLocked(task);
}
触发入口为:
//ActivityStack.java
//该方法由ActivityStackSupervisor 的 realStartActivityLocked中调用
void minimalResumeActivityLocked(ActivityRecord r) {
setResumedActivityLocked(r, "minimalResumeActivityLocked");
r.completeResumeLocked();
setLaunchTime(r);
}
private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
.......
setResumedActivityLocked(next, "resumeTopActivityInnerLocked");
.......
}
2.存储功能类
2.1.RecentTasks.java
负责将最近TaskRecord存入列表以及负责获取最近TaskRecord列表,在AMS中进行实例化;
RecentTasks(ActivityManagerService service, ActivityStackSupervisor mStackSupervisor) {
File systemDir = Environment.getDataSystemDirectory();
mService = service;
mTaskPersister = new TaskPersister(systemDir, mStackSupervisor, service, this);
mStackSupervisor.setRecentTasks(this);
}
在构造方法内部创建TaskPersister,然后将自己引用到 ActivityStackSupervisor内部,供后续调用;
void onSystemReadyLocked() {
clear();
mTaskPersister.startPersisting();
}
在onSystemReadyLocked()内部调用startPersisting()启动线程,接下来会讲到;
void notifyTaskPersisterLocked(TaskRecord task, boolean flush) {
final ActivityStack stack = task != null ? task.getStack() : null;
if (stack != null && stack.isHomeOrRecentsStack()) {
// Never persist the home or recents stack.
return;
}
syncPersistentTaskIdsLocked();
mTaskPersister.wakeup(task, flush);
}
每次有TaskRecord产生或变化时,都会调用notifyTaskPersisterLocked(),在其内部执行mTaskPersister的wakeup()进行相关的操作,接下来会讲到;
2.2.TaskPersister.java
负责将recent task信息存入/data/system_ce/0/recent_tasks里面以及从/data/system_ce/0/recent_tasks里面获取task信息,简单看一下主要代码:
TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
ActivityManagerService service, RecentTasks recentTasks) {
.............
mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
}
前面讲到,TaskPersister是在RecentTasks构造方法内创建的,也就是说,在RecentTasks创建的时候,TaskPersister也就被创建了,在构造方法内部会创建一个线程LazyTaskWriterThread来对TaskRecord进行存储操作;
void startPersisting() {
if (!mLazyTaskWriterThread.isAlive()) {
mLazyTaskWriterThread.start();
}
}
通过startPersisting()来启动LazyTaskWriterThread线程,接下来看一下LazyTaskWriterThread都干了什么工作;
private class LazyTaskWriterThread extends Thread {
LazyTaskWriterThread(String name) {
super(name);
}
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
ArraySet<Integer> persistentTaskIds = new ArraySet<Integer>();
while (true) {
.......
final boolean probablyDone;
synchronized (TaskPersister.this) {
probablyDone = mWriteQueue.isEmpty();
}
if (probablyDone) {
if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
persistentTaskIds.clear();
synchronized (mService) {
if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
for (int taskNdx = mRecentTasks.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mRecentTasks.get(taskNdx);
if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task +
" persistable=" + task.isPersistable);
final ActivityStack stack = task.getStack();
if ((task.isPersistable || task.inRecents)
&& (stack == null || !stack.isHomeOrRecentsStack())) {
if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
persistentTaskIds.add(task.taskId);
} else {
if (DEBUG) Slog.d(TAG,
"omitting from persistentTaskIds task=" + task);
}
}
mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
mRecentTasks.usersWithRecentsLoadedLocked());
}
removeObsoleteFiles(persistentTaskIds);
}
writeTaskIdsFiles();
processNextItem();
}
}
从线程的代码可以主要看到以下几点:
1.在run()内部有while(true),说明是该线程一直存在的;
2.对obsolete文件进行清理,包括recent_tasks、recent_images文件夹,通过WMS连间接调用TaskSnapshotPersister对snapshots文件夹进行清理;
3.在while(true)最后执行processNextItem(),因为是死循环,肯定不会一直执行,应该存在wait()操作,接下来一下看一下processNextItem()这个方法:
private void processNextItem() {
......
// If mNextWriteTime, then don't delay between each call to saveToXml().
final WriteQueueItem item;
synchronized (TaskPersister.this) {
if (mNextWriteTime != FLUSH_QUEUE) {
// The next write we don't have to wait so long.
mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
if (DEBUG) Slog.d(TAG, "Next write time may be in " +
INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
}
while (mWriteQueue.isEmpty()) {
if (mNextWriteTime != 0) {
mNextWriteTime = 0; // idle.
TaskPersister.this.notifyAll(); // wake up flush() if needed.
}
try {
if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
TaskPersister.this.wait();
} catch (InterruptedException e) {
}
// Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
// from now.
}
item = mWriteQueue.remove(0);
long now = SystemClock.uptimeMillis();
if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
while (now < mNextWriteTime) {
try {
if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
(mNextWriteTime - now));
TaskPersister.this.wait(mNextWriteTime - now);
} catch (InterruptedException e) {
}
now = SystemClock.uptimeMillis();
}
// Got something to do.
}
.........
else if (item instanceof TaskWriteQueueItem) {
// Write out one task.
StringWriter stringWriter = null;
TaskRecord task = ((TaskWriteQueueItem) item).mTask;
if (DEBUG) Slog.d(TAG, "Writing task=" + task);
synchronized (mService) {
if (task.inRecents) {
// Still there.
try {
if (DEBUG) Slog.d(TAG, "Saving task=" + task);
stringWriter = saveToXml(task);
} catch (IOException e) {
} catch (XmlPullParserException e) {
}
}
}
if (stringWriter != null) {
// Write out xml file while not holding mService lock.
FileOutputStream file = null;
AtomicFile atomicFile = null;
try {
atomicFile = new AtomicFile(new File(
getUserTasksDir(task.userId),
String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
file = atomicFile.startWrite();
file.write(stringWriter.toString().getBytes());
file.write('\n');
atomicFile.finishWrite(file);
} catch (IOException e) {
if (file != null) {
atomicFile.failWrite(file);
}
Slog.e(TAG,
"Unable to open " + atomicFile + " for persisting. " + e);
}
}
}
}
}
}
会判断mWriteQueue是否为空,如果为空,说明没有任务队列,一直wait(),等待notify(),当被唤醒时,会从mWriteQueue()里面取出任务TaskWriteQueueItem,然后等待3S进行saveToXml[调用TaskRecord.java中的saveToXml,依次将task、TaskDescription、ThumbnailInfo、Intent等信息存入xml中],即把Task以xml形式存入/data/system_ce/0/recent_tasks内部,接下来看一下唤醒操作:
void wakeup(TaskRecord task, boolean flush) {
synchronized (this) {
if (task != null) {
int queueNdx;
for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
final WriteQueueItem item = mWriteQueue.get(queueNdx);
if (item instanceof TaskWriteQueueItem &&
((TaskWriteQueueItem) item).mTask == task) {
if (!task.inRecents) {
// This task is being removed.
removeThumbnails(task);
}
break;
}
}
if (queueNdx < 0 && task.isPersistable) {
mWriteQueue.add(new TaskWriteQueueItem(task));
}
} else {
// Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
// notified.
mWriteQueue.add(new WriteQueueItem());
}
if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
mNextWriteTime = FLUSH_QUEUE;
} else if (mNextWriteTime == 0) {
mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
}
if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
+ mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
+ " Callers=" + Debug.getCallers(4));
notifyAll();
}
yieldIfQueueTooDeep();
}
前面讲到,wakeUp()是在RecentTasks的notifyTaskPersisterLocked()方法调用的,在wakeUp()内部,由于Task不为null,因此会向mWriteQueue内部添加TaskWriteQueueItem(task),然后更新一下mNextWriteTime,最后执行notifyAll()唤醒LazyTaskWriterThread线程内部processNextItem()执行写操作;
将task存入Android系统以下目录中:
/data/system_ce/0/recent_tasks
该目录存储之前启动过的task,将task的信息以xx_task.xml保存,内容如下:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<task task_id="8" real_activity="com.android.settings/.Settings" real_activity_suspended="false" orig_activity="com.android.settings/.Settings" affinity="com.android.settings" root_has_reset="false" auto_remove_recents="false" asked_compat_mode="false" user_id="0" user_setup_complete="true" effective_uid="1000" task_type="0" first_active_time="1598295290496" last_active_time="1598295294829" last_time_moved="1598295290501" never_relinquish_identity="true" task_description_color="fff5f5f5" task_description_colorBackground="fffafafa" task_description_icon_filename="/data/system_ce/0/recent_images/8_activity_icon_1598295294794.png" task_thumbnailinfo_task_width="0" task_thumbnailinfo_task_height="0" task_thumbnailinfo_screen_orientation="0" task_affiliation_color="-657931" task_affiliation="8" prev_affiliation="-1" next_affiliation="-1" calling_uid="10021" calling_package="com.android.systemui" resize_mode="1" supports_picture_in_picture="false" privileged="true" min_width="-1" min_height="-1" persist_task_version="1">
<intent action="android.intent.action.MAIN" component="com.android.settings/.Settings" flags="14000000" />
</task>
七.Snapshot截取
1.截图时机
在TaskRecord发生变化时进行截取,简单来说:一个应用通常对应一个TaskRecord,那么截取时机就是应用退出后进行截取,具体的代码入口为:
void onTransitionStarting() {
handleClosingApps(mService.mClosingApps);
}
void notifyAppVisibilityChanged(AppWindowToken appWindowToken, boolean visible) {
if (!visible) {
handleClosingApps(Sets.newArraySet(appWindowToken));
}
}
private void handleClosingApps(ArraySet<AppWindowToken> closingApps) {
if (shouldDisableSnapshots()) {
return;
}
// We need to take a snapshot of the task if and only if all activities of the task are
// either closing or hidden.
getClosingTasks(closingApps, mTmpTasks);
snapshotTasks(mTmpTasks);
}
最终通过snapshotTasks()来进行截取对应TaskRecord的snapshot;
2.截图功能类
2.1.TaskSnapshotController.java
private void snapshotTasks(ArraySet<Task> tasks) {
for (int i = tasks.size() - 1; i >= 0; i--) {
final Task task = tasks.valueAt(i);
final int mode = getSnapshotMode(task);
final TaskSnapshot snapshot;
switch (mode) {
case SNAPSHOT_MODE_NONE:
continue;
case SNAPSHOT_MODE_APP_THEME:
snapshot = drawAppThemeSnapshot(task);
break;
case SNAPSHOT_MODE_REAL:
snapshot = snapshotTask(task);
break;
default:
snapshot = null;
break;
}
if (snapshot != null) {
//从snapshot获取GraphicBuffer
final GraphicBuffer buffer = snapshot.getSnapshot();
if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
buffer.destroy();
Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
+ buffer.getHeight());
} else {
//获取到有效的snapshot
//1.存入cache,供后续接口调用
mCache.putSnapshot(task, snapshot);
//2.通过TaskSnapshotPersister.java存入/data/system_ce/0/snapshots/
mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
if (task.getController() != null) {
//通知回调onSnapshotChanged()
task.getController().reportSnapshotChanged(snapshot);
}
}
}
}
}
①.根据Task来获取snapshot
//通过task获取到该task的snapshot
private TaskSnapshot snapshotTask(Task task) {
......
//获取GraphicBuffer
final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
-1, -1, false, scaleFraction, false, true);
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
return null;
}
//组合成TaskSnapshot
return new TaskSnapshot(buffer, top.getConfiguration().orientation,
minRect(mainWindow.mContentInsets, mainWindow.mStableInsets),
isLowRamDevice /* reduced */, scaleFraction /* scale */);
}
根据Task获取内部最顶层的AppWindowToken,在进行一系列计算操作获取截图对应的minLayer和maxLayer,最后调用SufaceControl对对应layer范围进行截图;
2.2.TaskSnapshotCache.java
②.将snapshot存入cache
//当task从recents里面移除后,在TaskRecord.java里面的removedFromRecents()方法里面会执行
//mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);最终会调用到该方法
//将cache及本地文件里面存储的图片文件删除
void notifyTaskRemovedFromRecents(int taskId, int userId) {
mCache.onTaskRemoved(taskId);
mPersister.onTaskRemovedFromRecents(taskId, userId);
}
void putSnapshot(Task task, TaskSnapshot snapshot) {
final CacheEntry entry = mRunningCache.get(task.mTaskId);
if (entry != null) {
mAppTaskMap.remove(entry.topApp);
}
final AppWindowToken top = task.getTopChild();
mAppTaskMap.put(top, task.mTaskId);
mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, task.getTopChild()));
}
2.3.TaskSnapshotPersister.java
③.将snapshot存文件
/**
* Persists a snapshot of a task to disk.
*/
void persistSnapshot(int taskId, int userId, TaskSnapshot snapshot) {
synchronized (mLock) {
mPersistedTaskIdsSinceLastRemoveObsolete.add(taskId);
sendToQueueLocked(new StoreWriteQueueItem(taskId, userId, snapshot));
}
}
private class StoreWriteQueueItem extends WriteQueueItem {
.................
@Override
void write() {
boolean failed = false;
if (!writeProto()) {
failed = true;
}
if (!writeBuffer()) {
failed = true;
}
if (failed) {
deleteSnapshot(mTaskId, mUserId);
}
}
.....................
}
最终通过StoreWriteQueueItem来存储snapshot,存储的对应目录为:
/data/system_ce/0/snapshots
存储了两份snapshot,格式为.jpg,一个是屏幕分辨率大小,另外一个是屏幕分辨率一半大小,对应如下:
15.jpg 15_reduced.jpg
3.回调onTaskSnapshotChanged()
通过AMS.registerTaskStackListener()来监听TaskSnapshotChanged()回调,当任务缩略图截取完成后可以进行相关逻辑处理,本文只画出调用流程图,代码就不贴了,详情可根据流程图参考源码来进一步了解:
image.png
八.删除Recents任务
进入最近任务列表后,可以删除已经打开的任务,入口为SystemServicesProxy.removeTask(id),调用流程如下:
//1.SystemServicesProxy.java
public void removeTask(final int taskId) {
if (mAm == null) return;
if (RecentsDebugFlags.Static.EnableMockTasks) return;
// Remove the task.
mUiOffloadThread.submit(() -> {
mAm.removeTask(taskId);
});
}
//2.ActivityManagerService.java
public boolean removeTask(int taskId) {
.......
try {
return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
//3.ActivityStackSupervisor.java
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
return removeTaskByIdLocked(taskId, killProcess, removeFromRecents, !PAUSE_IMMEDIATELY);
}
调用removeTaskByIdLocked()来对task进行清除;
boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents,
boolean pauseImmediately) {
final TaskRecord tr = anyTaskForIdLocked(taskId,MATCH_TASK_IN_STACKS_OR_RECENT_TASKS,
INVALID_STACK_ID);
if (tr != null) {
//TaskRecord 删除task里面的activity
tr.removeTaskActivitiesLocked(pauseImmediately);
//清理需要移除的task
cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
if (tr.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
return true;
}
Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
return false;
}
①.执行removeTaskActivitiesLocked()将TaskRecord内的mActivities逐一进行finish();
void removeTaskActivitiesLocked(boolean pauseImmediately) {
performClearTaskAtIndexLocked(0, pauseImmediately);
}
final void performClearTaskAtIndexLocked(int activityNdx,
boolean pauseImmediately) {
int numActivities = mActivities.size();
for ( ; activityNdx < numActivities; ++activityNdx) {
final ActivityRecord r = mActivities.get(activityNdx);
if (r.finishing) {
continue;
}
if (mStack == null) {
// Task was restored from persistent storage.
r.takeFromHistory();
mActivities.remove(activityNdx);
--activityNdx;
--numActivities;
} else if (mStack.finishActivityLocked(r, Activity.RESULT_CANCELED,
null, "clear-task-index", false, pauseImmediately)) {
--activityNdx;
--numActivities;
}
}
}
②.执行cleanUpRemovedTaskLocked()来后续清理工作
void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
if (removeFromRecents) {
//通过RecentTasks删除task的相关信息
mRecentTasks.remove(tr);
//将TaskRecord从Recents里面移除
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.
mService.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<>();
//系统中正在run的进程
ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.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);
...............
//不满足以上条件后,加入procToKill list中
// Add process to kill list.
procsToKill.add(proc);
}
}
// Kill the running processes.
//是否立即kill process
for (int i = 0; i < procsToKill.size(); i++) {
ProcessRecord pr = procsToKill.get(i);
if ((pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
&& pr.curReceivers.isEmpty())) {
pr.kill("remove task", true);
} else {
// We delay killing processes that are not in the background or running a receiver.
pr.waitingToKill = "remove task";
}
}
}
③.通知TaskPersister和TaskSnapshotController删除存储的TaskRecord信息
void removedFromRecents() {
disposeThumbnail();
closeRecentsChain();
if (inRecents) {
inRecents = false;
//通过以下方法最终会调用到删除task(../recent_tasks)的逻辑
mService.notifyTaskPersisterLocked(this, false);
}
//通过以下方法最终会调用TaskSnapshotController删除snapshot(../snapshots)的逻辑
mService.mWindowManager.notifyTaskRemovedFromRecents(taskId, userId);
}
//TaskSnapshotController.java
void notifyTaskRemovedFromRecents(int taskId, int userId) {
//将cache及本地文件里面存储的图片文件删除
mCache.onTaskRemoved(taskId);
mPersister.onTaskRemovedFromRecents(taskId, userId);
}
④.通过ProcessRecord来kill进程
void kill(String reason, boolean noisy) {
if (!killedByAm) {
.......
//通过Process kill掉进程
Process.killProcessQuiet(pid);
ActivityManagerService.killProcessGroup(uid, pid);
......
}
}
九.启动Recents任务
当进入多任务后,点击列表中的某个item可以进入对应的应用页面,入口是在SystemServiceProxy.java里面的startActivityFromRecents(),然后一步一步调用AMS、ASS、AS来执行启动逻辑,通过taskId来找到对应的task,通过task找到对应的ActivityStack及task里面的topActivity,经过一系列操作后最后将Activity推到栈顶启动,流程图如下:
本文大概介绍了Recents工作流程,深入了解的话需要阅读源码!
关于Android 11,可以参考Android11 最近任务Recents功能分析