Execute deactivation callbacks for LayoutNode before disposing effects
Ensures that layout node deactivation is performed before composition disposal. In cases when Modifier.Node uses values that are owned by composition, node should be reset before the owned value is disposed by composition.
Test: Existing tests
Fixes: 277124390
Change-Id: I5957617e9f15af8d581b5cf304f5312bba310169
diff --git a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
index cb4e9ee..77eeb588 100644
--- a/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
+++ b/compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/Composition.kt
@@ -623,7 +623,6 @@
}
applier.clear()
manager.dispatchRememberObservers()
- manager.dispatchNodeCallbacks()
}
manager.dispatchAbandons()
}
@@ -797,7 +796,6 @@
writer.removeCurrentGroup(manager)
}
manager.dispatchRememberObservers()
- manager.dispatchNodeCallbacks()
}
private fun applyChangesInLocked(changes: MutableList<Change>) {
@@ -822,7 +820,6 @@
// that implement RememberObserver receive onRemembered before a side effect
// that captured it and operates on it can run.
manager.dispatchRememberObservers()
- manager.dispatchNodeCallbacks()
manager.dispatchSideEffects()
if (pendingInvalidScopes) {
@@ -1102,6 +1099,18 @@
}
fun dispatchRememberObservers() {
+ // Send node deactivations
+ val deactivating = deactivating
+ if (!deactivating.isNullOrEmpty()) {
+ trace("Compose:deactivations") {
+ for (i in deactivating.size - 1 downTo 0) {
+ val instance = deactivating[i]
+ instance.onDeactivate()
+ }
+ }
+ deactivating.clear()
+ }
+
// Send forgets
if (forgetting.isNotEmpty()) {
trace("Compose:onForgotten") {
@@ -1123,6 +1132,20 @@
}
}
}
+
+ // Send node releases
+ val releasing = releasing
+ if (!releasing.isNullOrEmpty()) {
+ // note that in contrast with objects from `forgetting` we will invoke the callback
+ // even for objects being abandoned.
+ trace("Compose:releases") {
+ for (i in releasing.size - 1 downTo 0) {
+ val instance = releasing[i]
+ instance.onRelease()
+ }
+ }
+ releasing.clear()
+ }
}
fun dispatchSideEffects() {
@@ -1148,32 +1171,6 @@
}
}
}
-
- fun dispatchNodeCallbacks() {
- val deactivating = deactivating
- if (!deactivating.isNullOrEmpty()) {
- trace("Compose:deactivations") {
- for (i in deactivating.size - 1 downTo 0) {
- val instance = deactivating[i]
- instance.onDeactivate()
- }
- }
- deactivating.clear()
- }
-
- val releasing = releasing
- if (!releasing.isNullOrEmpty()) {
- // note that in contrast with objects from `forgetting` we will invoke the callback
- // even for objects being abandoned.
- trace("Compose:releases") {
- for (i in releasing.size - 1 downTo 0) {
- val instance = releasing[i]
- instance.onRelease()
- }
- }
- releasing.clear()
- }
- }
}
}
diff --git a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt
index 3a33b3a..cec085f 100644
--- a/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt
+++ b/tv/tv-foundation/src/androidTest/java/androidx/tv/foundation/lazy/list/LazyListTest.kt
@@ -602,7 +602,6 @@
.assertHeightIsEqualTo(150.dp)
}
- @Ignore("Disabled due to b/277124390")
@Test
fun whenNotAnymoreAvailableItemWasDisplayed() {
var items by mutableStateOf((1..30).toList())