void setState(ActivityState state, String reason) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "State movement: " + this + " from:" + getState()
+ " to:" + state + " reason:" + reason);
+
final ActivityState prev = mState;
+ final boolean stateChanged = prev != state;
+
mState = state;
- if (mState != prev) {
+ if (stateChanged) {
if (mRecentTransitions.size() == MAX_STORED_STATE_TRANSITIONS) {
mRecentTransitions.remove(0);
}
mRecentTransitions.add(new StateTransition(prev, state, reason));
}
+
+ if (stateChanged && isState(DESTROYING, DESTROYED)) {
+ makeFinishingLocked();
+
+ // When moving to the destroyed state, immediately destroy the activity in the
+ // associated stack. Most paths for finishing an activity will handle an activity's path
+ // to destroy through mechanisms such as ActivityStackSupervisor#mFinishingActivities.
+ // However, moving to the destroyed state directly (as in the case of an app dying) and
+ // marking it as finished will lead to cleanup steps that will prevent later handling
+ // from happening.
+ if (isState(DESTROYED)) {
+ final ActivityStack stack = getStack();
+ if (stack != null) {
+ stack.activityDestroyedLocked(this, reason);
+ }
+ }
+ }
}
ActivityState getState() {
* state to destroy so only the cleanup here is needed.
*
* Note: Call before #removeActivityFromHistoryLocked.
+ *
+ * @param r The {@link ActivityRecord} to cleanup.
+ * @param cleanServices Whether services bound to the {@link ActivityRecord} should also be
+ * cleaned up.
+ * @param destroy Whether the {@link ActivityRecord} should be destroyed.
+ * @param clearProcess Whether the client process should be cleared.
*/
- private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean setState) {
+ private void cleanUpActivityLocked(ActivityRecord r, boolean cleanServices, boolean destroy,
+ boolean clearProcess) {
onActivityRemovedFromStack(r);
r.deferRelaunchUntilPaused = false;
r.frozenBeforeDestroy = false;
- if (setState) {
+ if (destroy) {
if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to DESTROYED: " + r + " (cleaning up)");
r.setState(DESTROYED, "cleanupActivityLocked");
+ }
+
+ if (clearProcess) {
if (DEBUG_APP) Slog.v(TAG_APP, "Clearing app during cleanUp for activity " + r);
r.app = null;
}
boolean removedFromHistory = false;
- cleanUpActivityLocked(r, false, false);
+ cleanUpActivityLocked(r, false /* cleanServices */, false /* destroy */,
+ false /*clearProcess*/);
final boolean hadApp = r.app != null;
if (isInStackLocked(record) != null) {
if (record.isState(DESTROYING, DESTROYED)) {
- cleanUpActivityLocked(record, true, false);
+ cleanUpActivityLocked(record, true /* cleanServices */, false /* destroy */,
+ false /*clearProcess*/);
removeActivityFromHistoryLocked(record, reason);
}
}
r.icicle = null;
}
}
- cleanUpActivityLocked(r, true, true);
+ cleanUpActivityLocked(r, true /* cleanServices */, remove /* destroy */,
+ true /*clearProcess*/);
if (remove) {
removeActivityFromHistoryLocked(r, "appDied");
}
verify(mService.mStackSupervisor, times(1)).canPlaceEntityOnDisplay(anyInt(), eq(expected),
anyInt(), anyInt(), eq(record.info));
}
+
+ @Test
+ public void testFinishingAfterDestroying() throws Exception {
+ assertFalse(mActivity.finishing);
+ mActivity.setState(DESTROYING, "testFinishingAfterDestroying");
+ assertTrue(mActivity.isState(DESTROYING));
+ assertTrue(mActivity.finishing);
+ }
+
+ @Test
+ public void testFinishingAfterDestroyed() throws Exception {
+ assertFalse(mActivity.finishing);
+ mActivity.setState(DESTROYED, "testFinishingAfterDestroyed");
+ assertTrue(mActivity.isState(DESTROYED));
+ assertTrue(mActivity.finishing);
+ }
}