Coordinating ViewCompositionStrategy across nested ComposeView and AndroidView boundaries
0 reputation · 09 Sept 2021, 09:08 UTC
0 reputation · 09 Sept 2021, 09:08 UTC
When a ComposeView embedded in an XML layout hosts an AndroidView that itself renders a Compose subtree via a nested ComposeView, the two composition boundaries each require a ViewCompositionStrategy. The outer ComposeView typically uses DisposeOnViewTreeLifecycleDestroyed to survive configuration changes, while the inner ComposeView may need DisposeOnDetachedFromWindow to avoid leaking when the AndroidView is recycled in a RecyclerView.
The documentation does not specify how these strategies interact when the inner composition is created from within an AndroidView factory lambda. It is unclear whether the inner ComposeView inherits the outer ViewTreeLifecycleOwner, whether detachment of the AndroidView triggers disposal of the inner composition before the outer one, or whether a mismatch can leave a composition attached to a destroyed window.
What is the expected disposal order when the AndroidView is detached but the outer ComposeView remains attached? Does the inner ComposeView automatically adopt the outer ViewTreeLifecycleOwner, or must it be explicitly scoped? Can a single ViewCompositionStrategy be configured to handle both boundaries safely?
34775 reputation · 09 Sept 2021, 11:19 UTC
When an AndroidView hosts a nested ComposeView, the inner composition does not automatically inherit the ViewCompositionStrategy or the ViewTreeLifecycleOwner of the outer ComposeView. Each ComposeView instance maintains its own independent composition boundary.
If the AndroidView is detached from the window but the outer ComposeView remains attached, the inner composition will persist unless it has been explicitly configured with a strategy like DisposeOnDetachedFromWindow. The outer strategy (e.g., DisposeOnViewTreeLifecycleDestroyed) only triggers when the outer view's lifecycle owner is destroyed, which does not necessarily coincide with the detachment of a nested AndroidView.
The inner ComposeView attempts to find a ViewTreeLifecycleOwner by traversing up the view hierarchy. While it may eventually find the owner provided to the outer ComposeView, the disposal trigger is tied to the strategy assigned to that specific ComposeView instance. Because the inner view is created inside an AndroidView factory lambda, it is treated as a separate entity in the view tree.
To prevent memory leaks—particularly when the AndroidView is used within a RecyclerView—you must explicitly coordinate the strategies for both boundaries:
DisposeOnViewTreeLifecycleDestroyed to ensure the primary composition survives configuration changes but dies with the Activity/Fragment.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow). This ensures that when the AndroidView is recycled or removed, the inner composition is disposed of immediately, regardless of the outer composition's state.setViewTreeLifecycleOwner(owner) on the inner ComposeView before calling setContent.To verify that disposal is occurring in the correct order, implement a DisposableEffect in both the outer and inner compositions:
DisposableEffect(Unit) {
onDispose { Log.d("ComposeLeakCheck", "Composition Disposed: ${this.name}") }
}
Observe the Logcat during a RecyclerView scroll or a fragment transaction. If the inner composition log does not appear when the AndroidView leaves the screen, the inner strategy is incorrectly configured.
Missing Diagnostic Detail: Are you using a custom ViewTreeLifecycleOwner for the AndroidView, or are you relying on the default Activity/Fragment owner?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.