Interoperability with Jetpack Compose

Learn how to use Compose Interoperability in your Android app. By Eric Duenes.

5 (2) · 1 Review

Download materials
Save for later
Share

You’re close to completing your android application, and it’s all written using Android Views. You want to start using JetPack Compose, but don’t want to rewrite all your layout.xml files. Or, maybe you started a project using Jetpack Compose, but want to leverage existing Android Views. What can you do?

You’re in luck — Jetpack Compose offers Interoperability. You can take advantage of this modern toolkit one composable at a time and migrate in and out of your Android Views at your own pace.

In this tutorial, you’ll explore Compose interoperability. You’ll use composables to complete a scorekeeper app that was created using Android Views. You’ll change the playing field and add Android Views to an activity using Jetpack Compose. As you complete this tutorial, you’ll learn about:

  • Jetpack Compose Interoperability APIs
  • Composable Lifecycles
  • Composition Strategy
  • Recomposition

The match is about to start — may the best team win.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

Open the project with Android Studio. You’ll see the following file structure:

Project Setup

Project Setup

The AndroidManifest.xml declares .MainActivity as the activity to launch after the app starts, and it also contains .ComposeActivity — which currently does nothing. Using intent-filter attributes, you’ll be able to launch the main activity for the first half of the tutorial and the Compose activity for the second half. Here’s an overview of the two activities:

  • MainActivity.kt was developed using Android XML layout views and view binding. The activity inflates activity_main.xml layout view, and it provides the ability to keep score for the home team. These are the files you’ll be manipulating in the first half of the tutorial.
  • ComposeActivity.kt was developed using Jetpack Compose. It will leverage components from VisitorTeamView.kt and provides the ability to keep score for the home team. You’ll be using these files in the second part of the tutorial.

Sync the project. Then, build and run. The app will look like this:

Starting Screen for the Skeeper Application

Starting Screen for the Skeeper Application

The starter project gives your user the ability to keep score for the home team — but sports matches are usually played by two teams. Using Jetpack Compose interoperability, you’ll add functionality to keep score for the visiting team.

Introducing Recomposition Strategies

Before you start keeping score, consider the lifecycle of a composable.

A composable can go through many changes during the lifecycle of your application. It’s drawn on your screen when initialized and re-drawn whenever the state changes. Additionally, when using Interoperability API, a composable can become detached and disposed of Android Views at different stages of the view lifecycle. As a result, it’s best to identify and set the correct ViewCompositionStrategy.

By setting the correct view composition strategy, you’re ensuring two things:

  • Your components don’t become disposed of while still on the screen. That would be a bad user experience.
  • Your components do become disposed of at the right time. Retaining components unnecessarily will cause memory leaks in your application.

There are three view composition strategy options available:

  1. DisposeOnDetachedFromWindow: This is the default strategy, and the component will be disposed when the view is detached. This strategy can cause a memory leak if you call createComposition(). In this scenario, the view will get created but might never be attached, so it won’t have the opportunity to be disposed.
  2. DisposeOnLifecycleDestroyed: With this strategy, the component will be destroyed when the owner of the lifecycle is destroyed.
  3. DisposeOnViewTreeLifecycleDestroyed: In this strategy, the component will be destroyed when the parent’s lifecycle is destroyed. This strategy is ideal when your component is in a Fragment.

Adding Composables

From the starter project, open activity_main.xml. You’ll add the Visitor Team section using ComposeView.

After the divider view, add the following code:

<androidx.compose.ui.platform.ComposeView
      android:id="@+id/compose_view"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginTop="40dp"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toBottomOf="@id/divider"
  />

You’ve just given yourself an entry point to add composables to your Android View.

Now, open MainActivity.kt and inside onCreate(), use view binding to apply the composable like this:

//1
binding.composeView.apply {
  //2
  setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnDetachedFromWindow)
  //3
  setContent {
   //4
    val visitorScore = remember { mutableStateOf(0) }
    //5
    VisitorScreenView(
      visitorScore = visitorScore.value,
      decreaseScore = { visitorScore.value-- },
      increaseScore = { visitorScore.value++ })
  }
}

Score one for the good guys… yes, that’s a sports reference. :]

You’ve now applied VisitorScreenView composable to the Android View. Here’s what you did:

  • visitorScore variable accepts an integer value that will be used to display the score for the visiting team.
  • decreaseScore parameter accepts a lambda function. It will be used to decrease the visiting team score.
  • increaseScore parameter accepts a lambda function. It will be used to increase the visiting team’s score.
  1. Using view binding, you’re able to get a handle to ComposeView. Remember? You added that to your layout XML file.
  2. Here, you’re setting the composition strategy to DisposeOnDetachedFromWindow. This strategy is safe to use here since your composables will be displayed in an activity, and you won’t be calling createComposition() anywhere in your code.
  3. This is the block where you’ll add your composables.
  4. You’ll keep track of the score with visitorScore. This will be stored as state and will be used by VisitorScreenView. To learn more about preserving the UI state of an Android application, check out Managing State in Jetpack Compose.
  5. VisitorScreenView is a composable function that has already been created for you. It contains the view for the visiting team and takes three parameters:

Build and run the application. You’ll see this screen, and you’ll be able to modify the visiting team’s score.

Added Visitor View to Skeeper App

Added Visitor View to Skeeper App

Abstracting Composables

You’ve now successfully added composables to your view, and you’re keeping score like a champ. But as the game progresses, you realize something… you can track the score, but with your Android Views and composables in the same file, how can you track where Android Views end and composables begin?

To keep things clean, you should separate your composables from your Android Views. You can use AbstractComposeView to achieve this.

Open VisitorTeamView.kt and add the following code:

class VisitorTeamView @JvmOverloads constructor(
  context: Context,
  attrs: AttributeSet? = null,
  defStyleAttr: Int = 0
  ) : AbstractComposeView(context, attrs, defStyleAttr) {
  
  //1
  private val visitorScore =  mutableStateOf(0)

  //2
  @Composable
  override fun Content() {
    setViewCompositionStrategy(
      ViewCompositionStrategy.DisposeOnDetachedFromWindow )

    VisitorScreenView(
      visitorScore = visitorScore.value,
      decreaseScore = { visitorScore.value-- },
      increaseScore = { visitorScore.value++ })

  }
}

VisitorTeamView class implements AbstractComposeView. The code should look familiar to you because it’s similar to what you implemented in the previous step.

  1. Your visitorScore variable will hold the score value as state.
  2. Inside Content() is where you’ll set your composition strategy and call your composable functions.
Note: There’s no need to use view binding because you’ll call the VisitorTeamView class directly from the layout file.

Open activity_main.xml and replace androidx.compose.ui.platform.ComposeView declaration with com.yourcompany.skeeper.ui.VisitorTeamView:

<com.yourcompany.skeeper.ui.VisitorTeamView
  android:id="@+id/compose_view"
  ...
/>

Finally, to complete the cleanup, go back to MainActivity and remove binding.composeView.apply {} along with all the lines of code inside of it.

There you have it. All your composables are in the com.yourcompany.skeeper.ui package.

Build and run your application to see that it looks and behaves the same way.