安卓生命周期失效导致的问题

183 阅读12分钟

笔者在适配安卓16的时候发现一个奇怪的bug,桌面在编辑页面的时候有概率会卡住点不动,问题现象如下:

image.png

操作如下即可:桌面编辑模式,点击壁纸后上滑,再点击小部件后上滑,此时点击小部件/壁纸/桌面设置均无反应

按照描述尝试去复现这个问题,发现了一些小细节

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日志,我们发现了疑点

1758682031073.jpg

1758682048279.jpg

可以看到这次的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)

这种方式也可以修复此问题,但是不是很合理,一方面这种本身业务侧导致的问题系统兜底不太合适,另一个方面可能会对性能产生小影响