Merge "Let activities can be recreated in ON_START state"

This commit is contained in:
TreeHugger Robot
2020-08-26 02:58:16 +00:00
committed by Android (Google) Code Review
3 changed files with 77 additions and 3 deletions

View File

@@ -5418,13 +5418,12 @@ public final class ActivityThread extends ClientTransactionHandler {
final int prevState = r.getLifecycleState();
if (prevState < ON_RESUME || prevState > ON_STOP) {
Log.w(TAG, "Activity state must be in [ON_RESUME..ON_STOP] in order to be relaunched,"
if (prevState < ON_START || prevState > ON_STOP) {
Log.w(TAG, "Activity state must be in [ON_START..ON_STOP] in order to be relaunched,"
+ "current state is " + prevState);
return;
}
// Initialize a relaunch request.
final MergedConfiguration mergedConfiguration = new MergedConfiguration(
r.createdConfig != null ? r.createdConfig : mConfiguration,

View File

@@ -185,6 +185,9 @@ public class TransactionExecutorHelper {
final ActivityLifecycleItem lifecycleItem;
switch (prevState) {
// TODO(lifecycler): Extend to support all possible states.
case ON_START:
lifecycleItem = StartActivityItem.obtain();
break;
case ON_PAUSE:
lifecycleItem = PauseActivityItem.obtain();
break;

View File

@@ -23,6 +23,8 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME;
import static android.app.servertransaction.ActivityLifecycleItem.ON_START;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -30,7 +32,11 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
@@ -50,6 +56,7 @@ import android.os.Binder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.testing.PollingCheck;
import android.view.WindowManagerGlobal;
import androidx.test.annotation.UiThreadTest;
@@ -63,6 +70,8 @@ import org.mockito.Mockito;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import java.util.concurrent.TimeUnit;
/**
* Test for verifying {@link android.app.ActivityThread} class.
*
@@ -76,6 +85,7 @@ import org.mockito.quality.Strictness;
@MediumTest
@Presubmit
public class ActivityThreadClientTest {
private static final long WAIT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2);
@Test
@UiThreadTest
@@ -152,6 +162,63 @@ public class ActivityThreadClientTest {
}
}
@Test
public void testLifecycleOfRelaunch() throws Exception {
try (ClientMockSession clientSession = new ClientMockSession()) {
ActivityThread activityThread = clientSession.mockThread();
ActivityClientRecord r = clientSession.stubActivityRecord();
final TestActivity[] activity = new TestActivity[1];
// Verify for ON_CREATE state. Activity should not be relaunched.
getInstrumentation().runOnMainSync(() -> {
activity[0] = (TestActivity) clientSession.launchActivity(r);
});
recreateAndVerifyNoRelaunch(activityThread, activity[0]);
// Verify for ON_START state. Activity should be relaunched.
getInstrumentation().runOnMainSync(() -> clientSession.startActivity(r));
recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_START);
// Verify for ON_RESUME state. Activity should be relaunched.
getInstrumentation().runOnMainSync(() -> clientSession.resumeActivity(r));
recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_RESUME);
// Verify for ON_PAUSE state. Activity should be relaunched.
getInstrumentation().runOnMainSync(() -> clientSession.pauseActivity(r));
recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_PAUSE);
// Verify for ON_STOP state. Activity should be relaunched.
getInstrumentation().runOnMainSync(() -> clientSession.stopActivity(r));
recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_STOP);
// Verify for ON_DESTROY state. Activity should not be relaunched.
getInstrumentation().runOnMainSync(() -> clientSession.destroyActivity(r));
recreateAndVerifyNoRelaunch(activityThread, activity[0]);
}
}
private void recreateAndVerifyNoRelaunch(ActivityThread activityThread, TestActivity activity) {
clearInvocations(activityThread);
getInstrumentation().runOnMainSync(() -> activity.recreate());
verify(activityThread, after(WAIT_TIMEOUT_MS).never())
.handleRelaunchActivity(any(), any());
}
private void recreateAndVerifyRelaunched(ActivityThread activityThread, TestActivity activity,
ActivityClientRecord r, int expectedState) throws Exception {
clearInvocations(activityThread);
getInstrumentation().runOnMainSync(() -> activity.recreate());
verify(activityThread, timeout(WAIT_TIMEOUT_MS)).handleRelaunchActivity(any(), any());
// Wait for the relaunch to complete.
PollingCheck.check("Waiting for the expected state " + expectedState + " timeout",
WAIT_TIMEOUT_MS,
() -> expectedState == r.getLifecycleState());
assertEquals(expectedState, r.getLifecycleState());
}
private class ClientMockSession implements AutoCloseable {
private MockitoSession mMockSession;
private ActivityThread mThread;
@@ -200,6 +267,11 @@ public class ActivityThreadClientTest {
false /* getNonConfigInstance */, "test");
}
private ActivityThread mockThread() {
spyOn(mThread);
return mThread;
}
private ActivityClientRecord stubActivityRecord() {
ComponentName component = new ComponentName(
InstrumentationRegistry.getInstrumentation().getContext(), TestActivity.class);