这里提到一种可能导致截图空白的场景->灭屏下的task变动
最近任务中保留的app截图,一般在当前应用从可见->不可见时进行截图
// For shell transition, record snapshots before transaction start.
void onTransactionReady(@WindowManager.TransitionType int type,
ArrayList<Transition.ChangeInfo> changeInfos) {
final boolean isTransitionOpen = isTransitionOpen(type);
final boolean isTransitionClose = isTransitionClose(type);
if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM) {
return;
}
ActivitiesByTask activityTargets = null;
for (int i = changeInfos.size() - 1; i >= 0; --i) {
Transition.ChangeInfo info = changeInfos.get(i);
// Intentionally skip record snapshot for changes originated from PiP.
if (info.mWindowingMode == WINDOWING_MODE_PINNED) continue;
if (info.mContainer.isActivityTypeHome()) continue;
final Task task = info.mContainer.asTask();
if (task != null && !task.mCreatedByOrganizer && !task.isVisibleRequested()) {
** // 一般从此处捕获,在shell动画的流程里**
mTaskSnapshotController.recordSnapshot(task, info);
}
// Won't need to capture activity snapshot in close transition.
if (isTransitionClose) {
continue;
}
if (info.mContainer.asActivityRecord() != null
|| info.mContainer.asTaskFragment() != null) {
final TaskFragment tf = info.mContainer.asTaskFragment();
final ActivityRecord ar = tf != null ? tf.getTopMostActivity()
: info.mContainer.asActivityRecord();
if (ar != null && ar.getTask().isVisibleRequested()) {
if (activityTargets == null) {
activityTargets = new ActivitiesByTask();
}
activityTargets.put(ar);
}
}
}
if (activityTargets != null) {
activityTargets.recordSnapshot(mActivitySnapshotController);
}
}
在该处调用到
mTaskSnapshotController.recordSnapshot(task, info);
这一段在shellTransition的逻辑里,一般是和动画有关系
recordSnapshot捕获截图的逻辑中,会提前进行check当前状态是否可捕获
/**
* Check if the state of the Task is appropriate to capture a snapshot, such like the task
* snapshot or the associated IME surface snapshot.
*
* @param source the target object to capture the snapshot
* @return Pair of (the top activity of the task, the main window of the task) if passed the
* state checking. Returns {@code null} if the task state isn't ready to snapshot.
*/
Pair<ActivityRecord, WindowState> checkIfReadyToSnapshot(TYPE source) {
**// 此处check是否灭屏**
if (!mService.mPolicy.isScreenOn()) {
if (DEBUG_SCREENSHOT) {
Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
}
return null;
}
... ...
return new Pair<>(activity, mainWindow);
}
第一条就是灭屏下禁止截图
所以灭屏下,不可截图,如果你的逻辑涉及灭屏瞬间有task变动,那很有可能会导致截图失败
但实际上灭屏前的瞬间,谷歌也有一段逻辑考虑了去捕获截图
/** Called when the device is going to sleep (e.g. screen off, AOD without screen off). */
void snapshotForSleeping(int displayId) {
if (shouldDisableSnapshots() || !mService.mDisplayEnabled) {
return;
}
final DisplayContent displayContent = mService.mRoot.getDisplayContent(displayId);
if (displayContent == null) {
return;
}
// Allow taking snapshot of home when turning screen off to reduce the delay of waking from
// secure lock to home.
final boolean allowSnapshotHome = displayId == Display.DEFAULT_DISPLAY
&& mService.mPolicy.isKeyguardSecure(mService.mCurrentUserId);
mTmpTasks.clear();
displayContent.forAllLeafTasks(task -> {
if (!allowSnapshotHome && task.isActivityTypeHome()) {
return;
}
// 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 (task.isVisible() && !isAnimatingByRecents(task)) {
mTmpTasks.add(task);
}
}, true /* traverseTopToBottom */);
**//此处即将去捕获截图**
snapshotTasks(mTmpTasks);
}
void snapshotTasks(ArraySet<Task> tasks) {
for (int i = tasks.size() - 1; i >= 0; i--) {
recordSnapshot(tasks.valueAt(i));
}
}
但比较遗憾的是
snapshotForSleeping是在灭屏瞬间被异步调用的,(毕竟捕获截图是个耗时操作
/**
* Called when screen is being turned off.
*/
void screenTurningOff(int displayId, ScreenOffListener listener) {
if (shouldDisableSnapshots()) {
listener.onScreenOff();
return;
}
// We can't take a snapshot when screen is off, so take a snapshot now!
mHandler.post(() -> {
try {
synchronized (mService.mGlobalLock) {
snapshotForSleeping(displayId);
}
} finally {
listener.onScreenOff();
}
});
}
这意味着当流程走到真正截图前的check工作时,很有可能已经灭屏,导致截图请求被驳回
所以尽量不要在灭屏时触及移动task变动,如果设计到该场景,需要额外在checkIfReadyToSnapshot方法里面加豁免