Skip to content

Commit 7a8ac55

Browse files
committed
Refactor LifecycleOwner to ComposeLifecycleOwner for better lifecycle management
- Extracted anonymous LifecycleOwner and RememberObserver implementation into a reusable ComposeLifecycleOwner class. - ComposeLifecycleOwner synchronizes its lifecycle with the parent Lifecycle and integrates with Compose's memory model via RememberObserver. - Improves code readability, reusability, and aligns lifecycle management with Compose best practices.
1 parent b2e04e8 commit 7a8ac55

File tree

2 files changed

+77
-33
lines changed

2 files changed

+77
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.squareup.workflow1.ui.compose
2+
3+
import androidx.compose.runtime.RememberObserver
4+
import androidx.lifecycle.Lifecycle
5+
import androidx.lifecycle.LifecycleEventObserver
6+
import androidx.lifecycle.LifecycleOwner
7+
import androidx.lifecycle.LifecycleRegistry
8+
9+
/**
10+
* A custom [LifecycleOwner] that synchronizes its lifecycle with a parent [Lifecycle] and
11+
* integrates with Jetpack Compose's lifecycle through [RememberObserver].
12+
*
13+
* **Purpose:**
14+
*
15+
* - Ensures that any lifecycle-aware components within a composable function have a lifecycle that
16+
* accurately reflects both the parent lifecycle and the composable's own lifecycle.
17+
* - Manages lifecycle transitions and observer registration/removal to prevent memory leaks and
18+
* ensure proper cleanup when the composable leaves the composition.
19+
*
20+
* **Key Features:**
21+
*
22+
* - **Lifecycle Synchronization:** Mirrors lifecycle events from the provided `parentLifecycle` to
23+
* its own [LifecycleRegistry], ensuring consistent state transitions.
24+
* - **Compose Integration:** Implements [RememberObserver] to align with the composable's lifecycle
25+
* in the Compose memory model.
26+
* - **Automatic Observer Management:** Adds and removes a [LifecycleEventObserver] to the parent
27+
* lifecycle, preventing leaks and ensuring proper disposal.
28+
* - **State Transition Safety:** Carefully manages lifecycle state changes to avoid illegal
29+
* transitions, especially during destruction.
30+
*
31+
* **Usage Notes:**
32+
*
33+
* - Should be used in conjunction with `remember` and provided the `parentLifecycle` as a key to
34+
* ensure it updates correctly when the parent lifecycle changes.
35+
* - By integrating with Compose's lifecycle, it ensures that resources are properly released when
36+
* the composable leaves the composition.
37+
*
38+
* @param parentLifecycle The parent [Lifecycle] with which this lifecycle owner should synchronize.
39+
*/
40+
internal class ComposeLifecycleOwner(
41+
private val parentLifecycle: Lifecycle
42+
) : LifecycleOwner, RememberObserver {
43+
44+
private val registry = LifecycleRegistry(this)
45+
override val lifecycle: Lifecycle
46+
get() = registry
47+
48+
private val parentObserver = LifecycleEventObserver { _, event ->
49+
// Any time the parent lifecycle changes state, perform the same change on our lifecycle.
50+
registry.handleLifecycleEvent(event)
51+
}
52+
53+
init {
54+
// We do this right away to ensure that the parent lifecycle is observed as soon as possible
55+
// to prevent any inconsistencies.
56+
parentLifecycle.addObserver(parentObserver)
57+
}
58+
59+
override fun onRemembered() {
60+
61+
}
62+
63+
override fun onAbandoned() {
64+
onForgotten()
65+
}
66+
67+
override fun onForgotten() {
68+
parentLifecycle.removeObserver(parentObserver)
69+
70+
// If we're leaving the composition, ensure the lifecycle is cleaned up
71+
if (registry.currentState != Lifecycle.State.INITIALIZED) {
72+
registry.currentState = Lifecycle.State.DESTROYED
73+
}
74+
}
75+
}

workflow-ui/compose/src/main/java/com/squareup/workflow1/ui/compose/WorkflowRendering.kt

+2-33
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,11 @@ package com.squareup.workflow1.ui.compose
33
import androidx.compose.foundation.layout.Box
44
import androidx.compose.runtime.Composable
55
import androidx.compose.runtime.CompositionLocalProvider
6-
import androidx.compose.runtime.DisposableEffect
76
import androidx.compose.runtime.key
87
import androidx.compose.runtime.remember
98
import androidx.compose.ui.Modifier
109
import androidx.compose.ui.platform.LocalLifecycleOwner
11-
import androidx.lifecycle.Lifecycle
12-
import androidx.lifecycle.Lifecycle.State.DESTROYED
13-
import androidx.lifecycle.Lifecycle.State.INITIALIZED
14-
import androidx.lifecycle.LifecycleEventObserver
1510
import androidx.lifecycle.LifecycleOwner
16-
import androidx.lifecycle.LifecycleRegistry
1711
import com.squareup.workflow1.ui.Compatible
1812
import com.squareup.workflow1.ui.Screen
1913
import com.squareup.workflow1.ui.ScreenViewHolder
@@ -101,34 +95,9 @@ public fun WorkflowRendering(
10195
* bit simpler since we don't need to worry about attachment state.
10296
*/
10397
@Composable private fun rememberChildLifecycleOwner(): LifecycleOwner {
104-
val lifecycleOwner = remember {
105-
object : LifecycleOwner {
106-
val registry = LifecycleRegistry(this)
107-
override val lifecycle: Lifecycle
108-
get() = registry
109-
}
110-
}
11198
val parentLifecycle = LocalLifecycleOwner.current.lifecycle
112-
113-
DisposableEffect(parentLifecycle) {
114-
val parentObserver = LifecycleEventObserver { _, event ->
115-
// Any time the parent lifecycle changes state, perform the same change on our lifecycle.
116-
lifecycleOwner.registry.handleLifecycleEvent(event)
117-
}
118-
119-
parentLifecycle.addObserver(parentObserver)
120-
onDispose {
121-
parentLifecycle.removeObserver(parentObserver)
122-
123-
// If we're leaving the composition it means the WorkflowRendering is either going away itself
124-
// or about to switch to an incompatible rendering – either way, this lifecycle is dead. Note
125-
// that we can't transition from INITIALIZED to DESTROYED – the LifecycleRegistry will throw.
126-
// WorkflowLifecycleOwner has this same check.
127-
if (lifecycleOwner.registry.currentState != INITIALIZED) {
128-
lifecycleOwner.registry.currentState = DESTROYED
129-
}
130-
}
99+
val lifecycleOwner = remember(parentLifecycle) {
100+
ComposeLifecycleOwner(parentLifecycle)
131101
}
132-
133102
return lifecycleOwner
134103
}

0 commit comments

Comments
 (0)