笔者在适配安卓16的时候发现一个奇怪的bug,桌面在编辑页面的时候有概率会卡住点不动,问题现象如下:
操作如下即可:桌面编辑模式,点击壁纸后上滑,再点击小部件后上滑,此时点击小部件/壁纸/桌面设置均无反应
按照描述尝试去复现这个问题,发现了一些小细节
1.复现卡住必须要先点击壁纸在点击小部件,此时从小部件返回到桌面,小白色圆圈显示还在小部件,这个时候点击旁边的壁纸或者桌面摄制部都是点不动的 2.点击壁纸退出之后1秒内点击小部件必现,如果回到桌面超过1秒则不复现 3.壁纸不能点同意,必须在隐私同意页面退出才能复现
于是和桌面的开发者进行沟通过之后,这个小白圈的设置是一个标志位的体现,我们可以简单理解为以下方式(写过前端的同学可能比较熟悉):假设:在桌面初始化进去编辑模式的时候,标志位为0,然后点击下图哪个小部件标志位就设置为哪个,壁纸是1,小部件是2,桌面设置是3
此时我们返回桌面按道理标志位应该是0但是还是卡在小部件的2
通过沟通笔者确定了一点,这个标志位的设定是在应用执行onresume生命周期的时候
于是我们查看一下安卓15上面生命周期和安卓16的生命周期
15,对应正常流程:
09-03 19:52:56.990 1718 4819 I wm_pause_activity: [0,62693383,com.miui.home/.launcher.Launcher,userLeaving=true,pauseBackTasks]
09-03 19:52:57.017 1718 4507 I wm_pause_activity: [0,200941869,com.android.thememanager/.settings.ThemeAndWallpaperHomeEditDialogActivity,userLeaving=true,resumeTopActivity]
09-03 19:52:58.785 1718 3100 I wm_resume_activity: [0,62693383,2,com.miui.home/.launcher.Launcher]
09-03 19:52:59.845 1718 4507 I wm_pause_activity: [0,138073671,com.miui.securitycenter/com.miui.permcenter.permissions.SystemAppPermissionDialogActivity,userLeaving=true,finishTransition]
09-03 19:53:01.614 1718 4505 I wm_pause_activity: [0,62693383,com.miui.home/.launcher.Launcher,userLeaving=true,pauseBackTasks]
09-03 19:53:05.758 1718 4505 I wm_pause_activity: [0,13570687,com.miui.personalassistant/.picker.business.home.pages.PickerHomeActivity,userLeaving=false,finish]
09-03 19:53:05.775 1718 7894 I wm_resume_activity: [0,62693383,2,com.miui.home/.launcher.Launcher]
16,对应异常流程:
09-03 19:52:42.676 1718 3123 I wm_pause_activity: [0,62693383,com.miui.home/.launcher.Launcher,userLeaving=true,pauseBackTasks]
09-03 19:52:42.704 1718 3123 I wm_pause_activity: [0,219569051,com.android.thememanager/.settings.ThemeAndWallpaperHomeEditDialogActivity,userLeaving=true,resumeTopActivity]
09-03 19:52:43.841 1718 2958 I wm_resume_activity: [0,62693383,2,com.miui.home/.launcher.Launcher]
09-03 19:52:44.892 1718 4817 I wm_pause_activity: [0,106491156,com.miui.securitycenter/com.miui.permcenter.permissions.SystemAppPermissionDialogActivity,userLeaving=true,finishTransition]
09-03 19:52:46.643 1718 4505 I wm_pause_activity: [0,235424048,com.miui.personalassistant/.picker.business.home.pages.PickerHomeActivity,userLeaving=false,finish]
查看对应生命周期我们发现,这里的onresume Launcher时是少了一次的
为什么这里的resume没有执行呢?
activity启动过程中resume的执行关键函数是“resumeTopActivity”
首先去查看对应函数实现细节
final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
boolean skipPause) {
ActivityRecord next = topRunningActivity(true /* focusableOnly */);
if (next == null || !next.canResumeByCompat()) {
return false;
}
next.delayedResume = false;
if (!skipPause && !mRootWindowContainer.allPausedActivitiesComplete()) {
// If we aren't skipping pause, then we have to wait for currently pausing activities.
ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: some activity pausing.");
return false;
}
final TaskDisplayArea taskDisplayArea = getDisplayArea();
// If the top activity is the resumed one, nothing to do.
if (mResumedActivity == next && next.isState(RESUMED)
&& taskDisplayArea.allResumedActivitiesComplete()) {
// Ensure the visibility gets updated before execute app transition.
taskDisplayArea.ensureActivitiesVisible(null /* starting */, true /* notifyClients */);
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
executeAppTransition(options);
// In a multi-resumed environment, like in a freeform device, the top
// activity can be resumed, but it might not be the focused app.
// Set focused app when top activity is resumed. However, we shouldn't do it for a
// same task because it can break focused state. (e.g. activity embedding)
if (taskDisplayArea.inMultiWindowMode() && taskDisplayArea.mDisplayContent != null) {
final ActivityRecord focusedApp = taskDisplayArea.mDisplayContent.mFocusedApp;
if (focusedApp == null || focusedApp.getTask() != next.getTask()) {
taskDisplayArea.mDisplayContent.setFocusedApp(next);
}
}
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity "
+ "resumed %s", next);
return false;
}
// If we are sleeping, and there is no resumed activity, and the top activity is paused,
// well that is the state we want.
if (mLastPausedActivity == next && shouldSleepOrShutDownActivities()) {
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
executeAppTransition(options);
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Going to sleep and"
+ " all paused");
return false;
}
// Make sure that the user who owns this activity is started. If not,
// we will just leave it as is because someone should be bringing
// another user's activities to the top of the stack.
if (!mAtmService.mAmInternal.hasStartedUserState(next.mUserId)) {
Slog.w(TAG, "Skipping resume of top activity " + next
+ ": user " + next.mUserId + " is stopped");
return false;
}
// The activity may be waiting for stop, but that is no longer
// appropriate for it.
mTaskSupervisor.mStoppingActivities.remove(next);
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
mTaskSupervisor.setLaunchSource(next.info.applicationInfo.uid);
ActivityRecord lastResumed = null;
final Task lastFocusedRootTask = taskDisplayArea.getLastFocusedRootTask();
if (lastFocusedRootTask != null && lastFocusedRootTask != getRootTaskFragment().asTask()) {
// So, why aren't we using prev here??? See the param comment on the method. prev
// doesn't represent the last resumed activity. However, the last focus stack does if
// it isn't null.
lastResumed = lastFocusedRootTask.getTopResumedActivity();
}
boolean pausing = !skipPause && taskDisplayArea.pauseBackTasks(next);
if (mResumedActivity != null) {
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Pausing %s", mResumedActivity);
pausing |= startPausing(mTaskSupervisor.mUserLeaving, false /* uiSleeping */,
next, "resumeTopActivity");
}
if (pausing) {
ProtoLog.v(WM_DEBUG_STATES, "resumeTopActivity: Skip resume: need to"
+ " start pausing");
// At this point we want to put the upcoming activity's process
// at the top of the LRU list, since we know we will be needing it
// very soon and it would be a waste to let it get killed if it
// happens to be sitting towards the end.
if (next.attachedToProcess()) {
next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
true /* activityChange */, false /* updateOomAdj */,
false /* addPendingTopUid */);
} else if (!next.isProcessRunning()) {
// Since the start-process is asynchronous, if we already know the process of next
// activity isn't running, we can start the process earlier to save the time to wait
// for the current activity to be paused.
final boolean isTop = this == taskDisplayArea.getFocusedRootTask();
mAtmService.startProcessAsync(next, false /* knownToBeDead */, isTop,
isTop ? HostingRecord.HOSTING_TYPE_NEXT_TOP_ACTIVITY
: HostingRecord.HOSTING_TYPE_NEXT_ACTIVITY);
}
if (lastResumed != null) {
lastResumed.setWillCloseOrEnterPip(true);
}
return true;
} else if (mResumedActivity == next && next.isState(RESUMED)
&& taskDisplayArea.allResumedActivitiesComplete()) {
// It is possible for the activity to be resumed when we paused back stacks above if the
// next activity doesn't have to wait for pause to complete.
// So, nothing else to-do except:
// Make sure we have executed any pending transitions, since there
// should be nothing left to do at this point.
executeAppTransition(options);
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity resumed "
+ "(dontWaitForPause) %s", next);
return true;
}
// If the most recent activity was noHistory but was only stopped rather
// than stopped+finished because the device went to sleep, we need to make
// sure to finish it as we're making a new activity topmost.
if (shouldSleepActivities()) {
mTaskSupervisor.finishNoHistoryActivitiesIfNeeded(next);
}
if (prev != null && prev != next && next.nowVisible) {
// The next activity is already visible, so hide the previous
// activity's windows right now so we can show the new one ASAP.
// We only do this if the previous is finishing, which should mean
// it is on top of the one being resumed so hiding it quickly
// is good. Otherwise, we want to do the normal route of allowing
// the resumed activity to be shown so we can decide if the
// previous should actually be hidden depending on whether the
// new one is found to be full-screen or not.
if (prev.finishing) {
prev.setVisibility(false);
if (DEBUG_SWITCH) {
Slog.v(TAG_SWITCH, "Not waiting for visible to hide: " + prev
+ ", nowVisible=" + next.nowVisible);
}
} else {
if (DEBUG_SWITCH) {
Slog.v(TAG_SWITCH, "Previous already visible but still waiting to hide: " + prev
+ ", nowVisible=" + next.nowVisible);
}
}
}
try {
mTaskSupervisor.getActivityMetricsLogger()
.notifyBeforePackageUnstopped(next.packageName);
mAtmService.getPackageManagerInternalLocked().notifyComponentUsed(
next.packageName, next.mUserId,
next.packageName, next.toString()); /* TODO: Verify if correct userid */
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ next.packageName + ": " + e);
}
// We are starting up the next activity, so tell the window manager
// that the previous one will be hidden soon. This way it can know
// to ignore it when computing the desired screen orientation.
boolean anim = true;
final DisplayContent dc = taskDisplayArea.mDisplayContent;
if (prev != null) {
if (prev.finishing) {
if (DEBUG_TRANSITION) {
Slog.v(TAG_TRANSITION, "Prepare close transition: prev=" + prev);
}
if (mTaskSupervisor.mNoAnimActivities.contains(prev)) {
anim = false;
dc.prepareAppTransition(TRANSIT_NONE);
} else {
dc.prepareAppTransition(TRANSIT_CLOSE);
}
prev.setVisibility(false);
} else {
if (DEBUG_TRANSITION) {
Slog.v(TAG_TRANSITION, "Prepare open transition: prev=" + prev);
}
if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
dc.prepareAppTransition(TRANSIT_NONE);
} else {
dc.prepareAppTransition(TRANSIT_OPEN,
next.mLaunchTaskBehind ? TRANSIT_FLAG_OPEN_BEHIND : 0);
}
}
} else {
if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
if (mTaskSupervisor.mNoAnimActivities.contains(next)) {
anim = false;
dc.prepareAppTransition(TRANSIT_NONE);
} else {
dc.prepareAppTransition(TRANSIT_OPEN);
}
}
if (anim) {
next.applyOptionsAnimation();
} else {
next.abortAndClearOptionsAnimation();
}
mTaskSupervisor.mNoAnimActivities.clear();
if (next.attachedToProcess()) {
if (DEBUG_SWITCH) {
Slog.v(TAG_SWITCH, "Resume running: " + next + " stopped=" + next.mAppStopped
+ " visibleRequested=" + next.isVisibleRequested());
}
// If the previous activity is translucent, force a visibility update of
// the next activity, so that it's added to WM's opening app list, and
// transition animation can be set up properly.
// For example, pressing Home button with a translucent activity in focus.
// Launcher is already visible in this case. If we don't add it to opening
// apps, maybeUpdateTransitToWallpaper() will fail to identify this as a
// TRANSIT_WALLPAPER_OPEN animation, and run some funny animation.
final boolean lastActivityTranslucent = inMultiWindowMode()
|| mLastPausedActivity != null && !mLastPausedActivity.occludesParent();
// This activity is now becoming visible.
if (!next.isVisibleRequested() || next.mAppStopped || lastActivityTranslucent) {
next.app.addToPendingTop();
next.setVisibility(true);
}
// schedule launch ticks to collect information about slow apps.
next.startLaunchTickingLocked();
ActivityRecord lastResumedActivity =
lastFocusedRootTask == null ? null
: lastFocusedRootTask.getTopResumedActivity();
final ActivityRecord.State lastState = next.getState();
mAtmService.updateCpuStats();
ProtoLog.v(WM_DEBUG_STATES, "Moving to RESUMED: %s (in existing)", next);
next.setState(RESUMED, "resumeTopActivity");
// Activity should also be visible if set mLaunchTaskBehind to true (see
// ActivityRecord#shouldBeVisibleIgnoringKeyguard()).
if (shouldBeVisible(next)) {
// We have special rotation behavior when here is some active activity that
// requests specific orientation or Keyguard is locked. Make sure all activity
// visibilities are set correctly as well as the transition is updated if needed
// to get the correct rotation behavior. Otherwise the following call to update
// the orientation may cause incorrect configurations delivered to client as a
// result of invisible window resize.
// TODO: Remove this once visibilities are set correctly immediately when
// starting an activity.
final int originalRelaunchingCount = next.mPendingRelaunchCount;
mRootWindowContainer.ensureVisibilityAndConfig(next, mDisplayContent,
false /* deferResume */);
if (next.mPendingRelaunchCount > originalRelaunchingCount) {
// The activity is scheduled to relaunch, then ResumeActivityItem will be also
// included (see ActivityRecord#relaunchActivityLocked) if it should resume.
next.completeResumeLocked();
return true;
}
}
try {
final IApplicationThread appThread = next.app.getThread();
// Deliver all pending results.
final ArrayList<ResultInfo> a = next.results;
if (a != null) {
final int size = a.size();
if (!next.finishing && size > 0) {
if (DEBUG_RESULTS) {
Slog.v(TAG_RESULTS, "Delivering results to " + next + ": " + a);
}
final ActivityResultItem item = new ActivityResultItem(next.token, a);
mAtmService.getLifecycleManager().scheduleTransactionItem(appThread, item);
}
}
if (next.newIntents != null) {
final NewIntentItem item =
new NewIntentItem(next.token, next.newIntents, true /* resume */);
mAtmService.getLifecycleManager().scheduleTransactionItem(appThread, item);
}
// Well the app will no longer be stopped.
// Clear app token stopped state in window manager if needed.
next.notifyAppResumed();
EventLogTags.writeWmResumeActivity(next.mUserId, System.identityHashCode(next),
next.getTask().mTaskId, next.shortComponentName);
mAtmService.getAppWarningsLocked().onResumeActivity(next);
final int topProcessState = mAtmService.mTopProcessState;
next.app.setPendingUiCleanAndForceProcessStateUpTo(topProcessState);
next.abortAndClearOptionsAnimation();
final ResumeActivityItem resumeActivityItem = new ResumeActivityItem(
next.token, topProcessState, dc.isNextTransitionForward(),
next.shouldSendCompatFakeFocus());
mAtmService.getLifecycleManager().scheduleTransactionItem(
appThread, resumeActivityItem);
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next);
} catch (Exception e) {
// Whoops, need to restart this activity!
ProtoLog.v(WM_DEBUG_STATES, "Resume failed; resetting state to %s: "
+ "%s", lastState, next);
next.setState(lastState, "resumeTopActivityInnerLocked");
// lastResumedActivity being non-null implies there is a lastStack present.
if (lastResumedActivity != null) {
lastResumedActivity.setState(RESUMED, "resumeTopActivityInnerLocked");
}
Slog.i(TAG, "Restarting because process died: " + next);
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
} else if (SHOW_APP_STARTING_PREVIEW && lastFocusedRootTask != null
&& lastFocusedRootTask.isTopRootTaskInDisplayArea()) {
next.showStartingWindow(false /* taskSwitch */);
}
mTaskSupervisor.startSpecificActivity(next, true, false);
return true;
}
next.completeResumeLocked();
} else {
// Whoops, need to restart this activity!
if (!next.hasBeenLaunched) {
next.hasBeenLaunched = true;
} else {
if (SHOW_APP_STARTING_PREVIEW) {
next.showStartingWindow(false /* taskSwich */);
}
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
}
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Restarting %s", next);
mTaskSupervisor.startSpecificActivity(next, true, true);
}
return true;
}
方法比较长,大概有14个分支返回fasle,这里返回false表示此次resume请求不被允许
这里能否调试直接判断呢?不行
正常一次activity启动过程中这个方法会被回调很多次,因此我们可以通过加日志的方式判断是否返回有问题,这里有ProtoLog之类的日志打印,但是正常情况下是不会打印到bugreport中去的,因此我们需要复现并进行调试 首先设置日志打印
adb shell cmd window logging enable-text WM_DEBUG_STATES
其次进行复现查看,由于是多线程环境,发现其中异常时会多打印一次返回fasle
这里我们可以继续进行加日志定位,但是此时我们根据经验猜想launchUI的resume没有打印那么onpause是否也未执行,继续会看刚才的生命周期我们发现onpause也未执行,因此onpause应该才是最终原因
接着我们继续查看onpause的方法 在TaskFragment中有一个startPausing方法是执行pause的入口函数,
/** * Start pausing the currently resumed activity. It is an error to call this if there * is already an activity being paused or there is no resumed activity. * * @param userLeaving True if this should result in an onUserLeaving to the current activity. * @param uiSleeping True if this is happening with the user interface going to sleep (the * screen turning off). * @param resuming The activity we are currently trying to resume or null if this is not being * called as part of resuming the top activity, so we shouldn't try to instigate * a resume here if not null. * @param reason The reason of pausing the activity. * @return Returns true if an activity now is in the PAUSING state, and we are waiting for * it to tell us when it is done. */
boolean startPausing(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming,
String reason) {
if (!hasDirectChildActivities()) {
return false;
}
if (mResumedActivity != null && mTransitionController.isTransientLaunch(mResumedActivity)) {
// Even if the transient activity is occluded, defer pausing (addToStopping will still
// be called) it until the transient transition is done. So the current resuming
// activity won't need to wait for additional pause complete.
return false;
}
ProtoLog.d(WM_DEBUG_STATES, "startPausing: taskFrag =%s " + "mResumedActivity=%s", this,
mResumedActivity);
if (mPausingActivity != null) {
Slog.wtf(TAG, "Going to pause when pause is already pending for " + mPausingActivity
+ " state=" + mPausingActivity.getState());
if (!shouldSleepActivities()) {
// Avoid recursion among check for sleep and complete pause during sleeping.
// Because activity will be paused immediately after resume, just let pause
// be completed by the order of activity paused from clients.
completePause(false, resuming);
}
}
ActivityRecord prev = mResumedActivity;
if (prev == null) {
if (resuming == null) {
Slog.wtf(TAG, "Trying to pause when nothing is resumed");
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
return false;
}
if (prev == resuming) {
Slog.wtf(TAG, "Trying to pause activity that is in process of being resumed");
return false;
}
ProtoLog.v(WM_DEBUG_STATES, "Moving to PAUSING: %s", prev);
mPausingActivity = prev;
mLastPausedActivity = prev;
if (!prev.finishing && prev.isNoHistory()
&& !mTaskSupervisor.mNoHistoryActivities.contains(prev)) {
mTaskSupervisor.mNoHistoryActivities.add(prev);
}
prev.setState(PAUSING, "startPausingLocked");
prev.getTask().touchActiveTime();
mAtmService.updateCpuStats();
boolean pauseImmediately = false;
boolean shouldAutoPip = false;
if (resuming != null) {
// We do not want to trigger auto-PiP upon launch of a translucent activity.
final boolean resumingOccludesParent = resuming.occludesParent();
if (ActivityTaskManagerService.isPip2ExperimentEnabled()) {
// If a new task is being launched, then mark the existing top activity as
// supporting picture-in-picture while pausing only if the starting activity
// would not be considered an overlay on top of the current activity
// (eg. not fullscreen, or the assistant)
Task.enableEnterPipOnTaskSwitch(prev, resuming.getTask(),
resuming, resuming.getOptions());
}
// Resuming the new resume activity only if the previous activity can't go into Pip
// since we want to give Pip activities a chance to enter Pip before resuming the
// next activity.
final boolean lastResumedCanPip = prev.checkEnterPictureInPictureState(
"shouldAutoPipWhilePausing", userLeaving);
if (prev.supportsEnterPipOnTaskSwitch && userLeaving
&& resumingOccludesParent && lastResumedCanPip
&& prev.pictureInPictureArgs.isAutoEnterEnabled()) {
shouldAutoPip = true;
} else if (!lastResumedCanPip) {
// If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous
// activity to be paused.
pauseImmediately = (resuming.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
} else {
// The previous activity may still enter PIP even though it did not allow auto-PIP.
}
}
if (prev.attachedToProcess()) {
if (shouldAutoPip && ActivityTaskManagerService.isPip2ExperimentEnabled()) {
prev.mPauseSchedulePendingForPip = true;
boolean willAutoPip = mAtmService.prepareAutoEnterPictureAndPictureMode(prev);
ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, requesting PIP mode "
+ "via requestStartTransition(): %s, willAutoPip: %b", prev, willAutoPip);
} else if (shouldAutoPip) {
prev.mPauseSchedulePendingForPip = true;
boolean didAutoPip = mAtmService.enterPictureInPictureMode(
prev, prev.pictureInPictureArgs, false /* fromClient */);
ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode "
+ "directly: %s, didAutoPip: %b", prev, didAutoPip);
} else {
schedulePauseActivity(prev, userLeaving, pauseImmediately,
false /* autoEnteringPip */, reason);
}
} else {
mPausingActivity = null;
mLastPausedActivity = null;
mTaskSupervisor.mNoHistoryActivities.remove(prev);
}
// If we are not going to sleep, we want to ensure the device is
// awake until the next activity is started.
if (!uiSleeping && !mAtmService.isSleepingOrShuttingDownLocked()) {
mTaskSupervisor.acquireLaunchWakelock();
}
// If already entered PIP mode, no need to keep pausing.
if (mPausingActivity != null) {
// Have the window manager pause its key dispatching until the new
// activity has started. If we're pausing the activity just because
// the screen is being turned off and the UI is sleeping, don't interrupt
// key dispatch; the same activity will pick it up again on wakeup.
if (!uiSleeping) {
prev.pauseKeyDispatchingLocked();
} else {
ProtoLog.v(WM_DEBUG_STATES, "Key dispatch not paused for screen off");
}
if (pauseImmediately) {
// If the caller said they don't want to wait for the pause, then complete
// the pause now.
completePause(false, resuming);
return false;
} else {
prev.schedulePauseTimeout();
// All activities will be stopped when sleeping, don't need to wait for pause.
if (!uiSleeping) {
// Unset readiness since we now need to wait until this pause is complete.
mTransitionController.setReady(this, false /* ready */);
}
return true;
}
} else {
// This activity either failed to schedule the pause or it entered PIP mode,
// so just treat it as being paused now.
ProtoLog.v(WM_DEBUG_STATES, "Activity not running or entered PiP, resuming next.");
if (resuming == null) {
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
return false;
}
这里返回fasle代表不执行pause周期,直接查看ProtoLog日志,我们发现了疑点
可以看到这次的pause被跳过了,在代码中也有体现,这里我们查看这个具体逻辑 mResumedActivity表示已经拉起在前台的activty,resuming表示正在拉起的activity,因此这段的含义就是如果已经拉起的activity不为null并且这个拉起的activity有动画且正在执行动画则跳过这次pause
结合这段和上面的现象细节,我们判断这段应该是存在动画导致跳过了一次pause, 对比V上没有动画这段细节因此无问题, 至此这个问题原因比较明确,由于16上面的小改动导致了这个问题
这个问题的修复也是push业务侧去进行规避,系统侧要改动只能
boolean isPickerHome = resuming != null &&
"com.miui.personalassistant/.picker.business.home.pages.PickerHomeActivity"
.equals(resuming.shortComponentName);
boolean isLauncher = mResumedActivity != null &&
"com.miui.home/.launcher.Launcher"
.equals(mResumedActivity.shortComponentName);
boolean exempt = isPickerHome && isLauncher;
if (mResumedActivity != null
&& mTransitionController.isTransientLaunch(mResumedActivity)&&!exempt)
这种方式也可以修复此问题,但是不是很合理,一方面这种本身业务侧导致的问题系统兜底不太合适,另一个方面可能会对性能产生小影响