Compose and Traditional View Interoperability Strategy: Layered Decoupling in Practice

By Yue Sun · Platform Engineer • Published: 2025-10-28 • Updated: 2025-10-2813 min read

InteroperabilityMulti-moduleArchitecture Governance

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.
Note: Interop modules need separate R8 keep rules to preserve `androidx.compose` related classes, preventing accidental deletion during code shrinking.

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.

← Back to Blog