Ensure enqueueAction throws if fragment manager has no host
Added checks to enqueueAction to throw IllegalStateException if the
FragmentManager has been destroyed or it was never attached to a host.
Test: all tests pass
BUG: 64896609
Change-Id: Ie24565059f2ee7a54fda4c5253a13b3006db0994
diff --git a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.kt b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.kt
index a7bb60c..bfb0022 100644
--- a/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.kt
+++ b/fragment/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.kt
@@ -672,6 +672,62 @@
}
}
+ @Test
+ @UiThreadTest
+ fun popBackStackAfterManagerDestroyed() {
+ val viewModelStore = ViewModelStore()
+ val fc = activityRule.startupFragmentController(viewModelStore)
+ val fm = fc.supportFragmentManager
+
+ val fragment1 = StrictFragment()
+ fragment1.retainInstance = true
+ fm.beginTransaction()
+ .add(fragment1, "1")
+ .commitNow()
+
+ // Now destroy the Fragment Manager
+ fc.dispatchPause()
+ fc.dispatchStop()
+ fc.dispatchDestroy()
+
+ try {
+ fm.popBackStack()
+ fail("PopBackStack after FragmentManager destroyed should throw IllegalStateException")
+ } catch (e: IllegalStateException) {
+ assertWithMessage("popBackStack should throw an IllegalStateException")
+ .that(e)
+ .hasMessageThat()
+ .contains("FragmentManager has been destroyed")
+ }
+ }
+
+ @Test
+ @UiThreadTest
+ fun commitWhenFragmentManagerNeverAttached() {
+ val viewModelStore = ViewModelStore()
+ val fc = FragmentController.createController(
+ ControllerHostCallbacks(activityRule.activity, viewModelStore)
+ )
+ val fm = fc.supportFragmentManager
+
+ val fragment1 = StrictFragment()
+ fragment1.retainInstance = true
+
+ try {
+ fm.beginTransaction()
+ .add(fragment1, "1")
+ .commit()
+ fail("Commit when FragmentManager never attached should throw " +
+ "IllegalStateException")
+ } catch (e: IllegalStateException) {
+ assertWithMessage("Commit when FragmentManager never attached should throw an " +
+ "IllegalStateException")
+ .that(e)
+ .hasMessageThat()
+ .contains("FragmentManager has not been attached to a host.")
+ }
+ }
+
/**
* When a fragment is saved in non-config, it should be restored to the same index.
*/
diff --git a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
index cab38bf..20bc876 100644
--- a/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
+++ b/fragment/fragment/src/main/java/androidx/fragment/app/FragmentManager.java
@@ -2025,6 +2025,14 @@
*/
void enqueueAction(OpGenerator action, boolean allowStateLoss) {
if (!allowStateLoss) {
+ if (mHost == null) {
+ if (mDestroyed) {
+ throw new IllegalStateException("FragmentManager has been destroyed");
+ } else {
+ throw new IllegalStateException("FragmentManager has not been attached to a "
+ + "host.");
+ }
+ }
checkStateLoss();
}
synchronized (mPendingActions) {