Let activities can be recreated in ON_START state
We should also allow the activity to be recreated in ON_START state,
since ON_START is also a resting state and we don't see a reason to
prevent applications from doing so.
Bug: 162906202
Test: atest FrameworksMockingCoreTests
atest FrameworksCoreTests
atest WmTests
Change-Id: I20a9c1c25c0b5e1a392b18dc5bac26a1551990e2
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user