Jetpack Compose is a new toolkit for building native Android interfaces

You just describe what the interface looks like, and Compose does the rest

It’s compatible with existing interface toolkits, so you can mix and match classic views with new ones, and it supports Material and animation from the start.

In the Compose method, you need to add @compose to the method. All composable functions must carry this comment, which tells the Compose compiler that the function is intended to convert data into an interface.

Recombination is the process of calling a composable function again when the input changes

Compose can be efficiently recomposed by skipping all functions or lambdas whose parameters have not changed.

Compositing functions should not modify global variables. If other compositing functions depend on the global variable, there is a side effect that the compositing functions must be executed in a certain order, which is not what Compose wants.

If you need to perform an expensive operation (such as reading data from a shared preference setting), do it in the backend coroutine and pass the value result as a parameter to the composable function.

There are a number of things to be aware of when you program in Compose:

  • Composable functions can be executed in any order.
  • Composable functions can be executed in parallel.
  • Recombination skips as many composable functions and lambdas as possible.
  • Restructuring is an optimistic move and may be cancelled.
  • Composable functions may run as frequently as each frame of an animation.

Compose optimizes recombination by running composable functions in parallel

Since it can run in multiple threads, the function writes local variables, which is not thread-safe or correct code. This is because the interface can get messy when you call the change composition function in more than one place:

@Composable
@Deprecated("Example with bug")
fun ListWithBug(myList: List<String>) {
    var items = 0

    Row(horizontalArrangement = Arrangement.SpaceBetween) {
        Column {
            for (item in myList) {
                Text("Item: $item")
                items++ // Avoid! Side-effect of the column recomposing.
            }
        }
        Text("Count: $items")}}Copy the code

In this code, there may be two 2’s when items are multi-threaded, and the interface will have problems.

If some parts of the interface are not working, Compose will try to reorganize only the parts that need to be updated. This means that it can skip something to re-run the composable items of a single button without executing any composable items above or below it in the interface tree.

/** * Display a list of names the user can click with a header */
@Composable
fun NamePicker(
    header: String,
    names: List<String>,
    onNameClicked: (String) -> Unit
) {
    Column {
        // this will recompose when [header] changes, but not when [names] changes
        Text(header, style = MaterialTheme.typography.h5)
        Divider()

        // LazyColumn is the Compose version of a RecyclerView.
        // The lambda passed to items() is similar to a RecyclerView.ViewHolder.
        LazyColumn {
            items(names) { name ->
                // When an item's [name] updates, the adapter for that item
                // will recompose. This will not recompose when [header] changes
                NamePickerItem(name, onNameClicked)
            }
        }
    }
}

/** * Display a single name the user can click. */
@Composable
private fun NamePickerItem(name: String, onClicked: (String) -> Unit) {
    Text(name, Modifier.clickable(onClick = { onClicked(name) }))
}
Copy the code

When the header changes, Compose might jump to the Column lambda without executing any of its parents. In addition, Compose may choose to skip LazyColumnItems if names are not changed when Column is executed

Whenever Compose thinks the parameters of a composable item may have changed, it will begin the reorganization. The reorganization is an optimistic operation, which means Compose expects to complete the reorganization before the parameters change again. If a parameter changes before the reorganization is complete, Compose may cancel the reorganization and start over with the new parameter.

If your composable function needs data, it should define parameters for the corresponding data. You can then move the costly work to a thread other than the one that makes up the operation and pass the corresponding data to Compose using either mutableStateOf or LiveData.