Compose and Traditional View Interoperability Strategy: Layered Decoupling in Practice
Most enterprise applications will experience a long migration window, and Compose cannot be achieved overnight. During the coexistence phase, we need to build clear interoperability boundaries to ensure that the host module's lifecycle, theme system, and performance metrics are not disrupted.
We recommend starting with "read-only modules," introducing `ComposeView` as a bridge container, and mapping design tokens (colors, typography, spacing) to Compose `MaterialTheme` through a unified Design System Adapter.
Modularization Principles
- Separate bridge layer into its own module: `ui-interop` only handles View-Compose interaction, avoiding business modules directly depending on implementation details;
- Keep Compose entry points stateless: Pass data and callbacks through parameters, delegating state management to ViewModel or UseCase layers;
- Expose stable interfaces: Use `@Stable` data structures as boundary contracts to reduce diff noise from recomposition.
Bridge Layer Implementation Key Points
The following example shows how to safely host Compose in View modules while syncing host theme and lifecycle. We recommend using `ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed` to avoid memory leaks.
class ComposeInterstitial @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {
private val composeView = ComposeView(context).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
}
init {
addView(composeView)
}
fun render(state: InteropState, onAction: (InteropAction) -> Unit) {
composeView.setContent {
LegacyThemeAdapter {
InteropScreen(state = state, onAction = onAction)
}
}
}
}
@Stable
data class InteropState(
val title: String,
val illustrations: ImmutableList<Illustration>
)Monitoring and Performance Regression
Performance monitoring during the interoperability phase is particularly important. We record first frame time for both old and new interfaces in FrameMetricsAggregator and capture dropped frames using `Choreographer.FrameCallback`, writing metrics to Grafana.
When Compose module coverage exceeds 60%, consider migrating from `ComposeView` to full `Activity`/`Fragment` level to reduce additional measurement and layout costs.