Fix ConcurrentModificationException on the OnDestinationChangedListener
Go back to using the CopyOnWriteArrayList to ensure that
modifying the list of OnDestinationChangedListeners is safe.
RelNote: "You will no longer get a ConcurrentModificationException when
using OnDestinationChangedListeners."
Test: NavControllerTest
Bug: 188860458
Change-Id: Ib17074c773ed9462c6a69ad384c23e28fa8abaf6
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
index 71e4e6f..d3f9e3a 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerTest.kt
@@ -1956,6 +1956,27 @@
assertThat(backPressedIntercepted).isTrue()
}
+ @UiThreadTest
+ @Test
+ fun testOnDestinationChangedListenerConcurrentModification() {
+ val navController = createNavController()
+ navController.setGraph(R.navigation.nav_simple)
+
+ val listener = object : NavController.OnDestinationChangedListener {
+ override fun onDestinationChanged(
+ controller: NavController,
+ destination: NavDestination,
+ arguments: Bundle?
+ ) {
+ navController.removeOnDestinationChangedListener(this)
+ }
+ }
+
+ navController.addOnDestinationChangedListener(listener)
+ navController.addOnDestinationChangedListener { _, _, _ -> }
+ navController.navigate(R.id.second_test)
+ }
+
@Test
fun createGraph() {
val graph = navController.createGraph(startDestination = DESTINATION_ID) {
diff --git a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
index 009d740..e8209a6 100644
--- a/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
+++ b/navigation/navigation-runtime/src/main/java/androidx/navigation/NavController.kt
@@ -43,6 +43,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
+import java.util.concurrent.CopyOnWriteArrayList
/**
* NavController manages app navigation within a [NavHost].
@@ -104,8 +105,7 @@
private val backStackStates = mutableMapOf<String, ArrayDeque<NavBackStackEntryState>>()
private var lifecycleOwner: LifecycleOwner? = null
private var viewModel: NavControllerViewModel? = null
- private val >
- mutableListOf<OnDestinationChangedListener>()
+ private val >
private val lifecycleObserver: LifecycleObserver = LifecycleEventObserver { _, event ->
if (_graph != null) {