summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/FragmentHostCallback.java3
-rw-r--r--core/java/android/app/LoaderManager.java7
-rw-r--r--core/tests/coretests/AndroidManifest.xml2
-rw-r--r--core/tests/coretests/src/android/app/EmptyActivity.java21
-rw-r--r--core/tests/coretests/src/android/app/LoaderLifecycleTest.java225
5 files changed, 258 insertions, 0 deletions
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
index e1d713626e2e..b6aad3b15362 100644
--- a/core/java/android/app/FragmentHostCallback.java
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -340,6 +340,9 @@ public abstract class FragmentHostCallback<E> extends FragmentContainer {
}
void restoreLoaderNonConfig(ArrayMap<String, LoaderManager> loaderManagers) {
+ for (int i = 0, N = loaderManagers.size(); i < N; i++) {
+ ((LoaderManagerImpl) loaderManagers.valueAt(i)).updateHostController(this);
+ }
mAllLoaderManagers = loaderManagers;
}
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index c14dec93c348..bedf31a22f26 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -195,6 +195,9 @@ public abstract class LoaderManager {
public static void enableDebugLogging(boolean enabled) {
LoaderManagerImpl.DEBUG = enabled;
}
+
+ /** @hide for internal testing only */
+ public FragmentHostCallback getFragmentHostCallback() { return null; }
}
class LoaderManagerImpl extends LoaderManager {
@@ -542,6 +545,10 @@ class LoaderManagerImpl extends LoaderManager {
void updateHostController(FragmentHostCallback host) {
mHost = host;
}
+
+ public FragmentHostCallback getFragmentHostCallback() {
+ return mHost;
+ }
private LoaderInfo createLoader(int id, Bundle args,
LoaderManager.LoaderCallbacks<Object> callback) {
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 2452cfd66c3f..4416402d0717 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1147,6 +1147,8 @@
</activity>
<activity android:name="com.android.internal.policy.PhoneWindowActionModeTestActivity">
</activity>
+ <activity android:name="android.app.EmptyActivity">
+ </activity>
<receiver android:name="android.app.activity.AbortReceiver">
<intent-filter android:priority="1">
diff --git a/core/tests/coretests/src/android/app/EmptyActivity.java b/core/tests/coretests/src/android/app/EmptyActivity.java
new file mode 100644
index 000000000000..fefd7b724fed
--- /dev/null
+++ b/core/tests/coretests/src/android/app/EmptyActivity.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.app;
+
+public class EmptyActivity extends Activity {
+}
diff --git a/core/tests/coretests/src/android/app/LoaderLifecycleTest.java b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
new file mode 100644
index 000000000000..a3d51a0a9612
--- /dev/null
+++ b/core/tests/coretests/src/android/app/LoaderLifecycleTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.app;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArrayMap;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNotSame;
+import static junit.framework.TestCase.assertSame;
+
+@RunWith(AndroidJUnit4.class)
+public class LoaderLifecycleTest {
+ @Rule
+ public ActivityTestRule<EmptyActivity> mActivityRule =
+ new ActivityTestRule<>(EmptyActivity.class);
+ @Test
+ @MediumTest
+ public void loaderIdentityTest() throws Throwable{
+ mActivityRule.runOnUiThread(() -> {
+ final Handler h = new Handler();
+ final FragmentController fc1 = FragmentController.createController(
+ new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0));
+
+ fc1.attachHost(null);
+ fc1.dispatchCreate();
+
+ final FragmentManager fm1 = fc1.getFragmentManager();
+
+ final Fragment f1 = new Fragment();
+ fm1.beginTransaction().add(f1, "one").commitNow();
+
+ // Removing and re-adding a fragment completely will destroy its LoaderManager.
+ // Keep the first one here to confirm this later.
+ final LoaderManager lm1 = f1.getLoaderManager();
+
+ // Remove the fragment, add a second one, and re-add the first to
+ // force its internal index to change. The tests below should still remain consistent.
+ final Fragment f2 = new Fragment();
+ fm1.beginTransaction().remove(f1).commitNow();
+ fm1.beginTransaction().add(f2, "two").commitNow();
+ fm1.beginTransaction().add(f1, "one").commitNow();
+
+ // We'll check this to see if we get the same instance back later
+ // as passed through NonConfigurationInstance. If the keys stay consistent
+ // across fragment remove/re-add, this will be consistent.
+ final LoaderManager lm12 = f1.getLoaderManager();
+
+ assertNotSame("fully removed and re-added fragment got same LoaderManager", lm1, lm12);
+
+ fc1.dispatchActivityCreated();
+ fc1.noteStateNotSaved();
+ fc1.execPendingActions();
+ fc1.doLoaderStart();
+ fc1.dispatchStart();
+ fc1.reportLoaderStart();
+ fc1.dispatchResume();
+ fc1.execPendingActions();
+
+ // Bring the state back down to destroyed, simulating an activity restart
+ fc1.dispatchPause();
+ final Parcelable savedState = fc1.saveAllState();
+ fc1.doLoaderStop(true);
+ fc1.dispatchStop();
+ final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();
+
+ final ArrayMap<String, LoaderManager> loaderNonConfig = fc1.retainLoaderNonConfig();
+ assertNotNull("loaderNonConfig was null", loaderNonConfig);
+
+ fc1.dispatchDestroy();
+
+ // Create the new controller and restore state
+ final FragmentController fc2 = FragmentController.createController(
+ new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0));
+
+ final FragmentManager fm2 = fc2.getFragmentManager();
+
+ fc2.attachHost(null);
+ fc2.restoreLoaderNonConfig(loaderNonConfig);
+ fc2.restoreAllState(savedState, nonconf);
+ fc2.dispatchCreate();
+
+
+ fc2.dispatchActivityCreated();
+ fc2.noteStateNotSaved();
+ fc2.execPendingActions();
+ fc2.doLoaderStart();
+ fc2.dispatchStart();
+ fc2.reportLoaderStart();
+ fc2.dispatchResume();
+ fc2.execPendingActions();
+
+ // Test that the fragments are in the configuration we expect
+ final Fragment restoredOne = fm2.findFragmentByTag("one");
+ final LoaderManager lm2 = restoredOne.getLoaderManager();
+
+ assertSame("didn't get same LoaderManager instance back", lm2, lm12);
+
+ // Bring the state back down to destroyed before we finish the test
+ fc2.dispatchPause();
+ fc2.saveAllState();
+ fc2.dispatchStop();
+ fc2.dispatchDestroy();
+ });
+ }
+
+ @Test
+ @MediumTest
+ public void backStackLoaderIdentityTest() throws Throwable{
+ mActivityRule.runOnUiThread(() -> {
+ final Handler h = new Handler();
+ final FragmentHostCallback host1 =
+ new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0);
+ final FragmentController fc1 = FragmentController.createController(host1);
+
+ fc1.attachHost(null);
+ fc1.dispatchCreate();
+
+ final FragmentManager fm1 = fc1.getFragmentManager();
+
+ final Fragment f1 = new Fragment();
+ fm1.beginTransaction().add(f1, "one").commitNow();
+
+ final LoaderManager lm1 = f1.getLoaderManager();
+
+ // Put the fragment on the back stack.
+ fm1.beginTransaction().remove(f1).addToBackStack("backentry").commit();
+ fm1.executePendingTransactions();
+
+ fc1.dispatchActivityCreated();
+ fc1.noteStateNotSaved();
+ fc1.execPendingActions();
+ fc1.doLoaderStart();
+ fc1.dispatchStart();
+ fc1.reportLoaderStart();
+ fc1.dispatchResume();
+ fc1.execPendingActions();
+
+ // Bring the state back down to destroyed, simulating an activity restart
+ fc1.dispatchPause();
+ final Parcelable savedState = fc1.saveAllState();
+ fc1.doLoaderStop(true);
+ fc1.dispatchStop();
+ final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();
+
+ final ArrayMap<String, LoaderManager> loaderNonConfig = fc1.retainLoaderNonConfig();
+ assertNotNull("loaderNonConfig was null", loaderNonConfig);
+
+ fc1.dispatchDestroy();
+
+ // Create the new controller and restore state
+ final FragmentHostCallback host2 =
+ new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0);
+ final FragmentController fc2 = FragmentController.createController(host2);
+
+ final FragmentManager fm2 = fc2.getFragmentManager();
+
+ fc2.attachHost(null);
+ fc2.restoreLoaderNonConfig(loaderNonConfig);
+ fc2.restoreAllState(savedState, nonconf);
+ fc2.dispatchCreate();
+
+
+ fc2.dispatchActivityCreated();
+ fc2.noteStateNotSaved();
+ fc2.execPendingActions();
+ fc2.doLoaderStart();
+ fc2.dispatchStart();
+ fc2.reportLoaderStart();
+ fc2.dispatchResume();
+ fc2.execPendingActions();
+
+ assertNotSame("LoaderManager kept reference to old FragmentHostCallback",
+ host1, lm1.getFragmentHostCallback());
+ assertSame("LoaderManager did not refrence new FragmentHostCallback",
+ host2, lm1.getFragmentHostCallback());
+
+ // Test that the fragments are in the configuration we expect
+ final Fragment restoredOne = fm2.findFragmentByTag("one");
+ final LoaderManager lm2 = restoredOne.getLoaderManager();
+
+ assertSame("didn't get same LoaderManager instance back", lm2, lm1);
+
+ // Bring the state back down to destroyed before we finish the test
+ fc2.dispatchPause();
+ fc2.saveAllState();
+ fc2.dispatchStop();
+ fc2.dispatchDestroy();
+ });
+ }
+
+ public class TestFragmentHostCallback extends FragmentHostCallback<LoaderLifecycleTest> {
+ public TestFragmentHostCallback(Context context, Handler handler, int windowAnimations) {
+ super(context, handler, windowAnimations);
+ }
+
+ @Override
+ public LoaderLifecycleTest onGetHost() {
+ return LoaderLifecycleTest.this;
+ }
+ }
+}