应用切换至后台
因为应用从后台切换到前台需要使用快照填充启动窗口,所以需要在它进入后台时保存快照。应用的切换场会填充过度动画,task的快照流程的起点被设计在过渡动画的起点。
Back键退出应用
使用back键退出应用时Activity会被销毁,因此再次启动过应用时不属于热启动,不需要应用快照。但是多任务页面仍需要应用快照,所以只是移除应用快照的缓存。
从多任务退出应用
从多任务页面中移除应用时进程会被销毁,因此再次启动应用属于冷启动。此时没有任何场景需要再使用应用快照,所以将它的缓存和磁盘存储全部移除。
TaskSnapshot的创建在过渡动画开始时执行,大致流程为:准备过渡动画->执行过渡动画
当应用启动时和应用退到后台时,系统进程都会为应用准备过渡动画。
执行过渡动画的时机位于WMS的布局流程中,只有在窗口完成绘制之后,才能执行过渡动画。
以RootWindowContainer.java#performSurfacePlacementNoTrace()为起点,分析TaskSnapshot创建流程
void performSurfacePlacementNoTrace() {
......
final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
......
checkAppTransitionReady(surfacePlacer);
......
}
布局循环开始之时,先检查过渡动画是否准备好了
private void checkAppTransitionReady(WindowSurfacePlacer surfacePlacer) {
// Trace all displays app transition by Z-order for pending layout change.
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent curDisplay = mChildren.get(i);
// If we are ready to perform an app transition, check through all of the app tokens
// to be shown and see if they are ready to go.
if (curDisplay.mAppTransition.isReady()) {
// handleAppTransitionReady may modify curDisplay.pendingLayoutChanges.
curDisplay.mAppTransitionController.handleAppTransitionReady();
......
}
......
}
}
启动应用时prepare过渡动画,应用完成onresume时,execute过渡动画,所以此时可以handle过渡动画。即prepare-execute-handle
DisplayContent.java
mAppTransitionController = new AppTransitionController(mWmService, this);
AppTransitionController.java
void handleAppTransitionReady() {
......
mService.mTaskSnapshotController.onTransitionStarting(mDisplayContent);
}
此时动画已经配置到了窗口,通知TaskSnapshotController过渡动画正在开始。
紧接着上述代码流程
TaskSnapshotController.java
void onTransitionStarting(DisplayContent displayContent) {
handleClosingApps(displayContent.mClosingApps);
}
acitvityRecord设置可见性时,如果activityrecord即将不可见,那么会将activityRecord添加到displayContent.mClosingApps
private void handleClosingApps(ArraySet<ActivityRecord> 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);
mSkipClosingAppSnapshotTasks.clear();
}
/**
* Retrieves all closing tasks based on the list of closing apps during an app transition.
*/
@VisibleForTesting
void getClosingTasks(ArraySet<ActivityRecord> closingApps, ArraySet<Task> outClosingTasks) {
outClosingTasks.clear();
for (int i = closingApps.size() - 1; i >= 0; i--) {
final ActivityRecord activity = closingApps.valueAt(i);
final Task task = activity.getTask();
if (task == null) continue;
// Since RecentsAnimation will handle task snapshot while switching apps with the
// best capture timing (e.g. IME window capture),
// No need additional task capture while task is controlled by RecentsAnimation.
if (isAnimatingByRecents(task)) {
mSkipClosingAppSnapshotTasks.add(task);
}
// If the task of the app is not visible anymore, it means no other app in that task
// is opening. Thus, the task is closing.
if (!task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
outClosingTasks.add(task);
}
}
}
并不是所有的closingApps都需要进行截图。从getClosingTasks(closingApps, mTmpTasks)
的实现可以看出,截图是针对task的,只有即将不可见的task才需要截图。
snapshotTasks(mTmpTasks)
对过滤之后的closingApps进行缓存和持久化存储操作
从snapshotTasks()走到了recordTaskSnapshot()
void snapshotTasks(ArraySet<Task> tasks) {
snapshotTasks(tasks, false /* allowSnapshotHome */);
}
private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) {
for (int i = tasks.size() - 1; i >= 0; i--) {
recordTaskSnapshot(tasks.valueAt(i), allowSnapshotHome);
}
}
void recordTaskSnapshot(Task task, boolean allowSnapshotHome) {
final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
final TaskSnapshot snapshot = captureTaskSnapshot(task, snapshotHome);
final HardwareBuffer buffer = snapshot.getHardwareBuffer();
if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
buffer.close();
Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
+ buffer.getHeight());
} else {
//缓存snapshot
mCache.putSnapshot(task, snapshot);
// Don't persist or notify the change for the temporal snapshot.
if (!snapshotHome) {
//如果不是home类型的task,持久化存储snapshot
mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
task.onSnapshotChanged(snapshot);
}
}
}
1、根据不同的条件获取TaskSnapshot
2、缓存snapshot
3、持久化存储snapshot
/**
* This is different than {@link #recordTaskSnapshot(Task, boolean)} because it doesn't store
* the snapshot to the cache and returns the TaskSnapshot immediately.
*
* This is only used for testing so the snapshot content can be verified.
*/
@VisibleForTesting
TaskSnapshot captureTaskSnapshot(Task task, boolean snapshotHome) {
final TaskSnapshot snapshot;
if (snapshotHome) {
//allowSnapshotHome在本流程中为false,所以不会进入该分支。
//如果锁屏被设置了密码,那么在息屏流程中就会设置allowSnapshotHome为true,会进入该分支。
snapshot = snapshotTask(task);
} else {
switch (getSnapshotMode(task)) {
case SNAPSHOT_MODE_NONE:
return null;
case SNAPSHOT_MODE_APP_THEME:
//SNAPSHOT_MODE_APP_THEME模式表示不希望使用窗口的内容构建snapshot,只使用App的theme。
//例如被标记了WindowManager。LayoutParams.FLAG_SECURE的安全窗口;
//像PipMenuActivity设置了ATMS.setDisablePreviewScreenshots()
snapshot = drawAppThemeSnapshot(task);
break;
case SNAPSHOT_MODE_REAL:
//SNAPSHOT_MODE_REAL模式使用task的页面的内容构建snapshot
snapshot = snapshotTask(task);
break;
default:
snapshot = null;
break;
}
}
return snapshot;
}
@VisibleForTesting
int getSnapshotMode(Task task) {
final ActivityRecord topChild = task.getTopMostActivity();
if (!task.isActivityTypeStandardOrUndefined() && !task.isActivityTypeAssistant()) {
//如果ActivityType不是ACTIVITY_TYPE_UNDEFINED、ACTIVITY_TYPE_STANDARD、
//ACTIVITY_TYPE_ASSISTANT中的一种,那么就不会为其创建snapshot。
//从前面final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
//可以看出ACTIVITY_TYPE_HOME(即launcher)也是被排除在外的
return SNAPSHOT_MODE_NONE;
} else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
return SNAPSHOT_MODE_APP_THEME;
} else {
return SNAPSHOT_MODE_REAL;
}
}
我们分别来看看captureTaskSnapshot中的snapshot = drawAppThemeSnapshot(task);
和snapshot = snapshotTask(task);
/**
* If we are not allowed to take a real screenshot, this attempts to represent the app as best
* as possible by using the theme's window background.
*/
private TaskSnapshot drawAppThemeSnapshot(Task task) {
......
final int color = ColorUtils.setAlphaComponent(
task.getTaskDescription().getBackgroundColor(), 255);
......
final RenderNode node = RenderNode.create("TaskSnapshotController", null);
node.setLeftTopRightBottom(0, 0, width, height);
node.setClipToBounds(false);
final RecordingCanvas c = node.start(width, height);
c.drawColor(color);//设置页面颜色,color值从task背景色获取,alpha值255不透明
......
final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
......
// Note, the app theme snapshot is never translucent because we enforce a non-translucent
// color above
return new TaskSnapshot(
System.currentTimeMillis() /* id */,
topChild.mActivityComponent, hwBitmap.getHardwareBuffer(),
hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
contentInsets, letterboxInsets, false /* isLowResolution */,
false /* isRealSnapshot */, task.getWindowingMode(),
getAppearance(task), false /* isTranslucent */, false /* hasImeSurface */);
}
从Bitmap获取GraphicBuffer,作为TaskSnapshot的内容,所以drawAppThemeSnapshot()就是使用task背景色构建snapshot。
@Nullable
TaskSnapshot snapshotTask(Task task) {
return snapshotTask(task, PixelFormat.UNKNOWN);
}
@Nullable
TaskSnapshot snapshotTask(Task task, int pixelFormat) {
TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
......
//构建TaskSnapshot的核心还是在于GraphicBuffer的获取
final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
createTaskSnapshot(task, builder);
......
return builder.build();
}
@Nullable
SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
TaskSnapshot.Builder builder) {
Point taskSize = new Point();
final SurfaceControl.ScreenshotHardwareBuffer taskSnapshot = createTaskSnapshot(task,
mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize, builder);
builder.setTaskSize(taskSize);
return taskSnapshot;
}
@Nullable
SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
float scaleFraction, int pixelFormat, Point outTaskSize, TaskSnapshot.Builder builder) {
......
//使用Task的SurfaceControl获取GraphicBuffer,窗口的buffer本就是SurfaceControl提供的
final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
SurfaceControl.captureLayersExcluding(
task.getSurfaceControl(), mTmpRect, scaleFraction,
pixelFormat, excludeLayers);
......
return screenshotBuffer;
}
继续看recordTaskSnapshot方法中的mCache.putSnapshot(task, snapshot)
mCache是TaskSnapshotCache的对象
TaskSnapshotCache.java
void putSnapshot(Task task, TaskSnapshot snapshot) {
final CacheEntry entry = mRunningCache.get(task.mTaskId);
if (entry != null) {
//更新操作,为了将新的ActivityRecord的task绑定在一起
mAppTaskMap.remove(entry.topApp);
}
final ActivityRecord top = task.getTopMostActivity();
mAppTaskMap.put(top, task.mTaskId);
mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, top));
}
mAppTaskMap.put(top, task.mTaskId);
表示ActivityRecord和taskId建立映射关系
为什么不直接使用RunningCache,非要用两个map呢?这是为了监听通过ActivityRecord的remove来更新RunningCache。
mRunningCache.put(task.mTaskId, new CacheEntry(snapshot, top));
表示taskId和CacheEntry建立映射关系
为什么需要使用CacheEntry呢?这是应用需要通过taskId找到ActivityRecord,这样taskId和ActivityRecord完成了双向绑定。在ActivityRecord被移除时,可以顺着两个map移除snapshot;添加新的snapshot时,将旧的ActivityRecord移除,CacheEntry会直接被新的覆盖
Android的持久化存储基本都是使用Persister线程+Item队列的模式。
考虑到耗时和多线程的原因,创建一个线程用于处理各个场景创建的文件操作任务
把文件操作任务抽象成一个Item,这里WriteQueueItem。然后派生不同Item描述不同类型的操作
继续看recordTaskSnapshot方法中的mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
其中mPersister为TaskSnapshotPersister对象
TaskSnapshotPersister.java
/**
* Persists a snapshot of a task to disk.
*
* @param taskId The id of the task that needs to be persisted.
* @param userId The id of the user this tasks belongs to.
* @param snapshot The snapshot to persist.
*/
void persistSnapshot(int taskId, int userId, TaskSnapshot snapshot) {
synchronized (mLock) {
mPersistedTaskIdsSinceLastRemoveObsolete.add(taskId);
sendToQueueLocked(new StoreWriteQueueItem(taskId, userId, snapshot));
}
}
将TaskSnapshot持久化到磁盘
private class StoreWriteQueueItem extends WriteQueueItem {
......
@Override
void write() {
if (!createDirectory(mUserId)) {
Slog.e(TAG, "Unable to create snapshot directory for user dir="
+ getDirectory(mUserId));
}
boolean failed = false;
if (!writeProto()) {
failed = true;
}
if (!writeBuffer()) {
failed = true;
}
if (failed) {
deleteSnapshot(mTaskId, mUserId);
}
}
......
}
StoreWriteQueueItem描述了存储磁盘的任务,在重写的write方法中,会创建文件存储snapshot的task信息,buffer信息
@GuardedBy("mLock")
private void sendToQueueLocked(WriteQueueItem item) {
mWriteQueue.offer(item); //入队操作
item.onQueuedLocked();
ensureStoreQueueDepthLocked();
if (!mPaused) {
mLock.notifyAll();//唤醒Persist线程
}
}
private Thread mPersister = new Thread("TaskSnapshotPersister") {
public void run() {
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
WriteQueueItem next;
boolean isReadyToWrite = false;
synchronized (mLock) {
if (mPaused) {
next = null;
} else {
next = mWriteQueue.poll();
if (next != null) {
if (next.isReady()) {
isReadyToWrite = true;
next.onDequeuedLocked();
} else {
mWriteQueue.addLast(next);
}
}
}
}
if (next != null) {
if (isReadyToWrite) {
next.write();//取出Item任务后执行
}
SystemClock.sleep(DELAY_MS);
}
synchronized (mLock) {
final boolean writeQueueEmpty = mWriteQueue.isEmpty();
if (!writeQueueEmpty && !mPaused) {
continue;
}
try {
mQueueIdling = writeQueueEmpty;
mLock.wait();//执行完队列的所有任务进入等待状态,等待另一个线程来唤醒
mQueueIdling = false;
} catch (InterruptedException e) {
}
}
}
}
};
在什么时候需要移除TaskSnapshot?是在task被销毁的时候,两个移除场景
从back键退出应用和从多任务移除应用
一般来说,Activity对back事件的处理是移除Activity。TaskSnapshotController对Activity的移除比较感兴趣,所以在Activity被移除时TaskSnapshotController会尝试异常TaskSnapshot的缓存
ActivityClientController#activityDestroyed
class ActivityClientController extends IActivityClientController.Stub {
@Override
public void activityDestroyed(IBinder token) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
final long origId = Binder.clearCallingIdentity();
synchronized (mGlobalLock) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed");
try {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r != null) {
r.destroyed("activityDestroyed");
}
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Binder.restoreCallingIdentity(origId);
}
}
}
}
这里调用r.destroyed("activityDestroyed");
其中r为ActivityRecord对象
ActivityRecord.java
/**
* This method is to only be called from the client via binder when the activity is destroyed
* AND finished.
*/
void destroyed(String reason) {
removeDestroyTimeout();
ProtoLog.d(WM_DEBUG_CONTAINERS, "activityDestroyedLocked: r=%s", this);
if (!isState(DESTROYING, DESTROYED)) {
throw new IllegalStateException(
"Reported destroyed for activity that is not destroying: r=" + this);
}
if (isInRootTaskLocked()) {
cleanUp(true /* cleanServices */, false /* setState */);
removeFromHistory(reason);
}
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
调用removeFromHistory(reason);
/** Note: call {@link #cleanUp(boolean, boolean)} before this method. */
void removeFromHistory(String reason) {
finishActivityResults(Activity.RESULT_CANCELED,
null /* resultData */, null /* resultGrants */);
makeFinishingLocked();
ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s, reason= %s "
+ "callers=%s", this, reason, Debug.getCallers(5));
takeFromHistory();
removeTimeouts();
ProtoLog.v(WM_DEBUG_STATES, "Moving to DESTROYED: %s (removed from history)",
this);
setState(DESTROYED, "removeFromHistory");
if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during remove for activity " + this);
detachFromProcess();
// Resume key dispatching if it is currently paused before we remove the container.
resumeKeyDispatchingLocked();
mDisplayContent.removeAppToken(token);
cleanUpActivityServices();
removeUriPermissionsLocked();
}
调用mDisplayContent.removeAppToken(token);
为DisplayContent对象
DisplayContent.java
void removeAppToken(IBinder binder) {
final WindowToken token = removeWindowToken(binder, true /* animateExit */);
if (token == null) {
Slog.w(TAG_WM, "removeAppToken: Attempted to remove non-existing token: " + binder);
return;
}
final ActivityRecord activity = token.asActivityRecord();
if (activity == null) {
Slog.w(TAG_WM, "Attempted to remove non-App token: " + binder + " token=" + token);
return;
}
activity.onRemovedFromDisplay();
if (activity == mFixedRotationLaunchingApp) {
// Make sure the states of associated tokens are also cleared.
activity.finishFixedRotationTransform();
setFixedRotationLaunchingAppUnchecked(null);
}
}
调用activity.onRemovedFromDisplay()
,activity为ActivityRecord对象
ActivityRecord.java
void onRemovedFromDisplay() {
......
mWmService.mTaskSnapshotController.onAppRemoved(this);
......
}
最终调用到了TaskSnapshotController的onAppRemoved
TaskSnapshotController.java
/**
* Called when an {@link ActivityRecord} has been removed.
*/
void onAppRemoved(ActivityRecord activity) {
mCache.onAppRemoved(activity);
}
调用mCache.onAppRemoved(activity);
mCache为TaskSnapshotCache对象
TaskSnapshotCache.java
/**
* Called when an app token has been removed
*/
void onAppRemoved(ActivityRecord activity) {
final Integer taskId = mAppTaskMap.get(activity);
if (taskId != null) {
//断开ActivityRecord、taskId与TaskSnapshot之间的映射关系
removeRunningEntry(taskId);
}
}
void removeRunningEntry(int taskId) {
final CacheEntry entry = mRunningCache.get(taskId);
if (entry != null) {
mAppTaskMap.remove(entry.topApp);
mRunningCache.remove(taskId);
}
}
taskSnapshot的创建流程中,最后一次可见的ActivityRecord与taskId建立映射,所以当且仅当之前建立映射的ActivityRecord被移除时,才会移除缓存。
从多任务移除应用往往意味着这个task被移除了。即也不需要从热启动恢复这个task,不需要从多任务恢复这个task,那么TaskSnapshot的缓存和持久化存储都需要被移除
ActivityTaskManagerService#removeTask
public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
@Override
public boolean removeTask(int taskId) {
mAmInternal.enforceCallingPermission(REMOVE_TASKS, "removeTask()");
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
final Task task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task == null) {
Slog.w(TAG, "removeTask: No task remove with id=" + taskId);
return false;
}
if (task.isLeafTask()) {
mTaskSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task");
} else {
mTaskSupervisor.removeRootTask(task);
}
return true;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
}
调用mTaskSupervisor.removeTask(task, true, REMOVE_FROM_RECENTS, "remove-task");
mTaskSupervisor为ActivityTaskSupervisor对象
ActivityTaskSupervisor.java
void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
if (task.mInRemoveTask) {
// Prevent recursion.
return;
}
task.mTransitionController.requestCloseTransitionIfNeeded(task);
task.mInRemoveTask = true;
try {
task.removeActivities(reason, false /* excludingTaskOverlay */);
cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
mService.getLockTaskController().clearLockedTask(task);
mService.getTaskChangeNotificationController().notifyTaskStackChanged();
if (task.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
} finally {
task.mInRemoveTask = false;
}
}
调用了cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFromRecents) {
if (removeFromRecents) {
mRecentTasks.remove(task);
}
......
}
调用mRecentTasks.remove(task);
mRecentTasks为RecentTasks对象
RecentTasks.java
/**
* Remove a task from the recent tasks list.
*/
void remove(Task task) {
mTasks.remove(task);
notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */);
}
private void notifyTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess) {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess);
}
mTaskNotificationController.notifyTaskListUpdated();
}
interface Callbacks {
/**
* Called when a task is added to the recent tasks list.
*/
void onRecentTaskAdded(Task task);
/**
* Called when a task is removed from the recent tasks list.
*/
void onRecentTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess);
}
调用了mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess);
mCallbacks为Callbacks接口对象,该接口由ActivityTaskSupervisor实现
public class ActivityTaskSupervisor implements RecentTasks.Callbacks
因此onRecentTaskRemoved
方法的实现在ActivityTaskSupervisor中
ActivityTaskSupervisor.java
@Override
public void onRecentTaskRemoved(Task task, boolean wasTrimmed, boolean killProcess) {
if (wasTrimmed) {
// Task was trimmed from the recent tasks list -- remove the active task record as well
// since the user won't really be able to go back to it
removeTaskById(task.mTaskId, killProcess, false /* removeFromRecents */,
"recent-task-trimmed");
}
task.removedFromRecents();
}
这里调用了task.removedFromRecents();
task为Task对象
Task.java
void removedFromRecents() {
......
mAtmService.mWindowManager.mTaskSnapshotController.notifyTaskRemovedFromRecents(
mTaskId, mUserId);
}
最终调用到TaskSnapshotController的notifyTaskRemovedFromRecents()
TaskSnapshotController.java
void notifyTaskRemovedFromRecents(int taskId, int userId) {
mCache.onTaskRemoved(taskId);
mPersister.onTaskRemovedFromRecents(taskId, userId);
}
该方法做了两件事:移除缓存和移除持久化存储
TaskSnapshotCache.java
void onTaskRemoved(int taskId) {
removeRunningEntry(taskId);//移除两个map中的映射关系
}
void removeRunningEntry(int taskId) {
final CacheEntry entry = mRunningCache.get(taskId);
if (entry != null) {
mAppTaskMap.remove(entry.topApp);
mRunningCache.remove(taskId);
}
}
当task在最近任务被移除时,移除缓存
TaskSnapshotPersister.java
/**
* Callend when a task has been removed.
*
* @param taskId The id of task that has been removed.
* @param userId The id of the user the task belonged to.
*/
void onTaskRemovedFromRecents(int taskId, int userId) {
synchronized (mLock) {
//在之前的TaskSnapshot的持久化存储流程中有解锁Item队列会被Persist线程按序处理
mPersistedTaskIdsSinceLastRemoveObsolete.remove(taskId);
sendToQueueLocked(new DeleteWriteQueueItem(taskId, userId));
}
}
private class DeleteWriteQueueItem extends WriteQueueItem {
private final int mTaskId;
private final int mUserId;
DeleteWriteQueueItem(int taskId, int userId) {
mTaskId = taskId;
mUserId = userId;
}
@Override
void write() {
//通过taskId和userId找到TaskSnapshot文件存储的位置,并将其删除
deleteSnapshot(mTaskId, mUserId);
}
}
private void deleteSnapshot(int taskId, int userId) {
final File protoFile = getProtoFile(taskId, userId);
final File bitmapLowResFile = getLowResolutionBitmapFile(taskId, userId);
protoFile.delete();
if (bitmapLowResFile.exists()) {
bitmapLowResFile.delete();
}
final File bitmapFile = getHighResolutionBitmapFile(taskId, userId);
if (bitmapFile.exists()) {
bitmapFile.delete();
}
}