21

I'm currently studying Jetpack Compose in an attempt to build a feature-rich application using modern Android architecture components. Traditionally, each screen (or navigation unit) in my application would be either an activity or a fragment, each with its own lifecycle bindings, but with Jetpack Compose and the Compose Navigation library, I would do something like this:

MainActivity.kt:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val navController = rememberNavController()

            NavHost(navController = navController, startDestination = "main") {
                composable("main") { MainScreen(navController) }
                // More composable calls
            }
        }
    }
}

Where MainScreen is just a composable. My questions are:

  • What is the equivalent here of a "lifecycle" for this composable? Say I want to perform some action when the screen is loaded, when it is destroyed etc. This is perhaps more relevant to the case where I have more screens and navigation between them
  • Is there some way to integrate between Compose and standard activities? That is, define activities for screens as you would, with each activity being a ComponentActivity and defining its own composable layout? Is this discouraged for some reason?
Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
MrMikimn
  • 721
  • 1
  • 5
  • 19

1 Answers1

46

The Compose application is designed to be used in a single-activity architecture with no fragments.

You can still have multiple activities or fragments and use setContent in each of them, but in this case the transfer of data between activities falls on your shoulders. Use this approach if you're adding new Compose screens to an existing application built the old way.

But with Compose, it's much easier to do all the navigation within a single activity using Compose Navigation. Much less code, better performance due to no unnecessary code layers, easy to transfer data, etc.

To work with the view lifecycle, check out compose side-effects:

  1. LaunchedEffect can be used to execute an action when the view appears. It also runs on a coroutine context that is bound to the current composable: you can easily run suspend functions, and when the view disappears from view hierarchy - the coroutine will be canceled.
  2. DisposableEffect can be used to subscribe to/unsubscribe from callbacks.

When you rotate the screen, all effects will restart no matter which key you passed.

@Composable
fun MainScreen(navController: NavController) {
    LaunchedEffect(Unit) {
        println("LaunchedEffect: entered main")
        var i = 0
        // Just an example of coroutines usage
        // don't use this way to track screen disappearance
        // DisposableEffect is better for this
        try {
            while (true) {
                delay(1000)
                println("LaunchedEffect: ${i++} sec passed")
            }
        } catch (cancel: CancellationException) {
            println("LaunchedEffect: job cancelled")
        }
    }
    DisposableEffect(Unit) {
        println("DisposableEffect: entered main")
        onDispose {
            println("DisposableEffect: exited main")
        }
    }
}

Also note that in both cases, and in many other cases in compose, you pass key to these functions. This helps compose understand when the value should be recomputed. In my example it is Unit, which means that it won't change until the view is gone. But if you create a remember value, use another dynamic value from the view model, or pass another argument to composable, you can pass it as a key, this will cancel the current LaunchedEffect job and call onDispose for DisposableEffect, and your job will be restarted with the updated key value. You can pass as many keys as you want.

Read more about the state in Compose in documentation.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • 18
    `The Compose application is designed to be used in a single-activity architecture with no fragments.` is a very helpful statement. Just curious, where in any documentation did you see that? Want to see how I could miss such important statement. – Kevin Le - Khnle Nov 03 '21 at 16:12
  • 5
    @KevinLe-Khnle I'm not sure where I read this, but it seems very logical to me. You can do Compose navigation, and using fragments or multiple activities is much more complicated than implementing the same functionality with pure Compose. – Phil Dukhov Nov 03 '21 at 17:08
  • @PhilDukhov Indeed. When I read the docs on navigation (https://developer.android.com/jetpack/compose/navigation), my initial impression was the authors implied Compose was single-activity architecture. – Dorklord Sep 06 '22 at 16:19
  • @PhilDukhov If I was not mistaken, jetpack comppose is just like React.js, right? – Hamid Mohamadi Sep 01 '23 at 06:45