Handle stopping and finishing activities on drawn

If the non idle activity is started in a new task, there may have
a starting window. So the transition animation can be done with
the starting window but the main window of the activity may not
finish drawing if it is slow during startup. That causes the
allResumedActivitiesVisible to return false and the stopping
activities are still pending.

By scheduling to process stopping and finishing when the activity
windows are actually drawn (though its name is onWindowsVisible,
it is only called if the windows are drawn, see numVisible), the
pending stopping activities no longer need to wait until idle timeout
to be stopped or destroyed.

Also avoid sending duplicated idle message.

Bug: 169740664
Test: atest ActivityVisibilityTests# \
      testActivityStoppedWhileNextActivityNotIdle
Change-Id: I7317bf577a1b98601b6ec027a5d7bf66090aecde
This commit is contained in:
Riddle Hsu
2021-04-12 12:22:14 +08:00
parent 5683fda342
commit edc429403d
2 changed files with 16 additions and 19 deletions

View File

@@ -5862,6 +5862,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
nowVisible = true;
lastVisibleTime = SystemClock.uptimeMillis();
mAtmService.scheduleAppGcsLocked();
// The nowVisible may be false in onAnimationFinished because the transition animation
// was started by starting window but the main window hasn't drawn so the procedure
// didn't schedule. Hence also check when nowVisible becomes true (drawn) to avoid the
// closing activity having to wait until idle timeout to be stopped or destroyed if the
// next activity won't report idle (e.g. repeated view animation).
mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
}
}
@@ -6682,22 +6688,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
getDisplayContent().mAppTransition.notifyAppTransitionFinishedLocked(token);
scheduleAnimation();
if (!mTaskSupervisor.mStoppingActivities.isEmpty()
|| !mTaskSupervisor.mFinishingActivities.isEmpty()) {
if (mRootWindowContainer.allResumedActivitiesIdle()) {
// If all activities are already idle then we now need to make sure we perform
// the full stop of this activity. This is because we won't do that while they
// are still waiting for the animation to finish.
mTaskSupervisor.scheduleIdle();
} else if (mRootWindowContainer.allResumedActivitiesVisible()) {
// If all resumed activities are already visible (and should be drawn, see
// updateReportedVisibility ~ nowVisible) but not idle, we still schedule to
// process the stopping and finishing activities because the transition is done.
// This also avoids if the next activity never reports idle (e.g. animating view),
// the previous will need to wait until idle timeout to be stopped or destroyed.
mTaskSupervisor.scheduleProcessStoppingAndFinishingActivities();
}
}
// Schedule to handle the stopping and finishing activities which the animation is done
// because the activities which were animating have not been stopped yet.
mTaskSupervisor.scheduleProcessStoppingAndFinishingActivitiesIfNeeded();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}

View File

@@ -2027,7 +2027,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
final void scheduleIdle() {
mHandler.sendEmptyMessage(IDLE_NOW_MSG);
if (!mHandler.hasMessages(IDLE_NOW_MSG)) {
mHandler.sendEmptyMessage(IDLE_NOW_MSG);
}
}
/**
@@ -2115,8 +2117,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
}
void scheduleProcessStoppingAndFinishingActivities() {
if (!mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG)) {
void scheduleProcessStoppingAndFinishingActivitiesIfNeeded() {
if ((!mStoppingActivities.isEmpty() || !mFinishingActivities.isEmpty())
&& !mHandler.hasMessages(PROCESS_STOPPING_AND_FINISHING_MSG)
&& mRootWindowContainer.allResumedActivitiesVisible()) {
mHandler.sendEmptyMessage(PROCESS_STOPPING_AND_FINISHING_MSG);
}
}