Skip to content

Commit d4c2d5c

Browse files
Treehugger RobotGerrit Code Review
Treehugger Robot
authored and
Gerrit Code Review
committedApr 27, 2023
Merge "Ensure FragmentNavigator does not destroy entries early" into androidx-main
2 parents cac8837 + 320aa4c commit d4c2d5c

File tree

2 files changed

+46
-26
lines changed

2 files changed

+46
-26
lines changed
 

‎navigation/navigation-fragment/src/androidTest/java/androidx/navigation/fragment/NavControllerWithFragmentTest.kt

+25
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import androidx.lifecycle.Lifecycle
2424
import androidx.lifecycle.ViewModel
2525
import androidx.lifecycle.ViewModelProvider
2626
import androidx.navigation.NavOptions
27+
import androidx.navigation.createGraph
28+
import androidx.navigation.fragment.test.EmptyFragment
2729
import androidx.navigation.fragment.test.NavigationActivity
2830
import androidx.navigation.fragment.test.R
2931
import androidx.navigation.navOptions
@@ -284,6 +286,20 @@ class NavControllerWithFragmentTest {
284286
assertThat(fm.fragments.size).isEqualTo(2) // start + last dialog fragment
285287
}
286288

289+
@Test
290+
fun testPopEntryInFragmentResumed() = withNavigationActivity {
291+
navController.graph = navController.createGraph("first") {
292+
fragment<EmptyFragment>("first")
293+
fragment<PopInOnResumeFragment>("second")
294+
}
295+
navController.navigate("second")
296+
297+
val fm = supportFragmentManager.findFragmentById(R.id.nav_host)?.childFragmentManager
298+
fm?.executePendingTransactions()
299+
300+
assertThat(navController.currentBackStackEntry?.destination?.route).isEqualTo("first")
301+
}
302+
287303
private fun withNavigationActivity(
288304
block: NavigationActivity.() -> Unit
289305
) {
@@ -295,6 +311,15 @@ class NavControllerWithFragmentTest {
295311
}
296312
}
297313

314+
class PopInOnResumeFragment : Fragment(R.layout.strict_view_fragment) {
315+
override fun onResume() {
316+
super.onResume()
317+
findNavController().navigate("first") {
318+
popUpTo("first")
319+
}
320+
}
321+
}
322+
298323
class TestDialogFragment : DialogFragment() {
299324
val dialogs = mutableListOf<Dialog>()
300325

‎navigation/navigation-fragment/src/main/java/androidx/navigation/fragment/FragmentNavigator.kt

+21-26
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import androidx.fragment.app.FragmentManager.OnBackStackChangedListener
2929
import androidx.fragment.app.FragmentTransaction
3030
import androidx.lifecycle.Lifecycle
3131
import androidx.lifecycle.LifecycleEventObserver
32-
import androidx.lifecycle.LifecycleOwner
3332
import androidx.lifecycle.ViewModel
3433
import androidx.lifecycle.ViewModelProvider
3534
import androidx.lifecycle.viewmodel.CreationExtras
@@ -75,38 +74,34 @@ public open class FragmentNavigator(
7574
*/
7675
internal val backStack get() = state.backStack
7776

78-
private val fragmentObserver = object : LifecycleEventObserver {
79-
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
80-
if (event == Lifecycle.Event.ON_DESTROY) {
81-
val fragment = source as Fragment
82-
val entry = state.transitionsInProgress.value.lastOrNull { entry ->
83-
entry.id == fragment.tag
84-
}
85-
entry?.let {
86-
entriesToPop.remove(entry.id)
87-
state.markTransitionComplete(it)
77+
private val fragmentObserver = LifecycleEventObserver { source, event ->
78+
if (event == Lifecycle.Event.ON_DESTROY) {
79+
val fragment = source as Fragment
80+
val entry = state.transitionsInProgress.value.lastOrNull { entry ->
81+
entry.id == fragment.tag
82+
}
83+
if (entry != null) {
84+
entriesToPop.remove(entry.id)
85+
if (!state.backStack.value.contains(entry)) {
86+
state.markTransitionComplete(entry)
8887
}
89-
fragment.lifecycle.removeObserver(this)
9088
}
9189
}
9290
}
9391

9492
private val fragmentViewObserver = { entry: NavBackStackEntry ->
95-
object : LifecycleEventObserver {
96-
override fun onStateChanged(
97-
source: LifecycleOwner,
98-
event: Lifecycle.Event
99-
) {
100-
// Once the lifecycle reaches RESUMED, we can mark the transition complete
101-
if (event == Lifecycle.Event.ON_RESUME) {
102-
state.markTransitionComplete(entry)
103-
}
104-
// Once the lifecycle reaches DESTROYED, we can mark the transition complete and
105-
// remove the observer.
106-
if (event == Lifecycle.Event.ON_DESTROY) {
107-
entriesToPop.remove(entry.id)
93+
LifecycleEventObserver { _, event ->
94+
// Once the lifecycle reaches RESUMED, if the entry is in the back stack we can mark
95+
// the transition complete
96+
if (event == Lifecycle.Event.ON_RESUME && state.backStack.value.contains(entry)) {
97+
state.markTransitionComplete(entry)
98+
}
99+
// Once the lifecycle reaches DESTROYED, if the entry is not in the back stack, we can
100+
// mark the transition complete
101+
if (event == Lifecycle.Event.ON_DESTROY) {
102+
entriesToPop.remove(entry.id)
103+
if (!state.backStack.value.contains(entry)) {
108104
state.markTransitionComplete(entry)
109-
source.lifecycle.removeObserver(this)
110105
}
111106
}
112107
}

0 commit comments

Comments
 (0)
Please sign in to comment.