Android5.0新增了一个重启后可恢复Task功能。在正常的Activity切换使用过程中AMS会将Task和对应截图进行保存,重启后会将Task和截图恢复到最近任务栏中。开机恢复Task没什么好说的,我们重点研究下Task和截图的保存逻辑,如下。
我们重点分析下screenshotApplications()、notifyTaskPersisterLocked()、LazyTaskWriterThread线程。
1、screenshotApplications()
public Bitmap screenshotApplications(IBinder appToken, int displayId, int width,
int height, boolean force565) {
if (!checkCallingPermission(Manifest.permission.READ_FRAME_BUFFER,
"screenshotApplications()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent == null) {
if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
+ ": returning null. No Display for displayId=" + displayId);
return null;
}
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
int dw = displayInfo.logicalWidth;
int dh = displayInfo.logicalHeight;
if (dw == 0 || dh == 0) {
if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
+ ": returning null. logical widthxheight=" + dw + "x" + dh);
return null;
}
Bitmap bm = null;
int maxLayer = 0;
final Rect frame = new Rect();
final Rect stackBounds = new Rect();
float scale = 0;
int rot = Surface.ROTATION_0;
boolean screenshotReady;
int minLayer;
if (appToken == null) {
screenshotReady = true;
minLayer = 0;
} else {
screenshotReady = false;
minLayer = Integer.MAX_VALUE;
}
int retryCount = 0;
WindowState appWin = null;
final boolean appIsImTarget = mInputMethodTarget != null
&& mInputMethodTarget.mAppToken != null
&& mInputMethodTarget.mAppToken.appToken != null
&& mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
while (true) {
if (retryCount++ > 0) {
// Reset max/min layers on retries so we don't accidentally take a screenshot of a
// layer based on the previous try.
maxLayer = 0;
minLayer = Integer.MAX_VALUE;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
synchronized(mWindowMap) {
// Figure out the part of the screen that is actually the app.
appWin = null;
final WindowList windows = displayContent.getWindowList();
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState ws = windows.get(i);
if (!ws.mHasSurface) {
continue;
}
if (ws.mLayer >= aboveAppLayer) {
continue;
}
if (ws.mIsImWindow) {
if (!appIsImTarget) {
continue;
}
} else if (ws.mIsWallpaper) {
if (appWin == null) {
// We have not ran across the target window yet, so it is probably
// behind the wallpaper. This can happen when the keyguard is up and
// all windows are moved behind the wallpaper. We don't want to
// include the wallpaper layer in the screenshot as it will coverup
// the layer of the target window.