Compose Experience Sharing: Development Key Points & Common Mistakes & Interview Questions

1 Introduction

Since Compose is still in alpha, I have written three applications from zero to one with Compose: Twidere X Android, Mask-Android, and an undisclosed project.

https://github.com/TwidereProject/TwidereX-Android
https://github.com/DimensionDev/Mask-Android

Each application has different harvests. Now I will summarize the experience in it and share it with everyone. The article is not long but the words are detailed. If you don’t have time to read it, you can bookmark it first~

2. Summary

Let's just say a few key points:

  • One of the core ideas of Compose UI is: the state is down, the event is up, the state of the Compose UI component should come from its parameters instead of itself, do not do any calculations in the Compose UI component, there are a lot of performance problems actually come from the This core idea is not understood.
  • If a component has to hold some state internally, remember to use remember for all the variables in these states, because the Compose function will be executed very frequently, without remember, it will lead to frequent assignment and initialization, and even some calculation operations .
  • The parameters of Compose UI components should preferably be immutable, otherwise, at best, the expected performance will not be met, and at worst, performance will be affected.
  • Each Compose UI component should preferably have a Modifier, so that Compose UI components can be easily reused in different places.
  • For maintainability, please try to split the basic Compose UI components and business Compose UI components. The basic Compose UI components should be split as finely as possible. The business Compose UI components should be split as much as possible. You won’t If you want to maintain a Compose UI component with thousands of lines, subdivision will also increase the reuse rate to a certain extent.

The following summarizes some common incorrect usages, most of which will cause performance problems. Many people will say that Compose has poor performance, but in fact it is more of a wrong usage.

3. Misusing remember { mutableStateOf() }

One of the core ideas of Compose UI is: state goes down, events go up. This sentence may be better understood with an example. Generally, beginners will write code like this immediately after reading the tutorial:

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    Button(
        onClick = {
            count++
        }
    ) {
        Text("count $count")
    }
}

Then when the business logic is complicated, his code may look like this:


@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    var text by remember { mutableStateOf("") }

    Column {
        Button(
            onClick = {
                count++
            }
        ) {
            Text("count $count")
        }
        TextField(
            value = text,
            onValueChange = {
                text = it
            }
        )
        OtherCounter()
    }
}

@Composable
fun OtherCounter() {
    var text by remember { mutableStateOf("Hello world!") }
    Column {
        Text(text)
        TextField(
            value = text,
            onValueChange = {
                text = it
            }
        )
    }
}

Regardless of the business logic of the code, the Composable function here is stateful, which will bring unnecessary recomposition, which will lead to performance problems in the written Compose UI. According to the core idea, the state is downward, and the event is upward. The code should be written like this:


@Composable
fun CounterRoute(
    viewModel: CounterViewModel = viewModel<CounterViewModel>()
) {
    val state by viewModel.state.collectAsState()
    Counter(
        state = state,
        onIncrement = {
            viewModel.onIncrement()
        },
        onTextChange = {
            viewModel.onTextChange(it)
        },
        onOtherTextChange = {
            viewModel.onOtherTextChange(it)
        },
    )
}

@Composable
fun Counter(
    state: CounterState,
    onIncrement: () -> Unit,
    onTextChange: (String) -> Unit,
    onOtherTextChange: (String) -> Unit,
) {
    Column {
        Button(
            onClick = {
                onIncrement.invoke()
            }
        ) {
            Text("count ${state.count}")
        }
        TextField(
            value = state.text,
            onValueChange = {
                onTextChange.invoke(it)
            }
        )
        OtherCounter(
            text = state.otherText,
            onTextChange = onOtherTextChange,
        )
    }
}

@Composable
fun OtherCounter(
    text: String,
    onTextChange: (String) -> Unit,
) {
    Column {
        Text(text)
        TextField(
            value = text,
            onValueChange = {
                onTextChange.invoke(it)
            }
        )
    }
}

In this way, all states are placed on the top layer, and events are also handled by the top layer. Such a Compose UI component has no state, and such a Compose UI component will have very good performance.

4. forget to remember

Just said abuse, now say forget. When a component has to hold state internally, remember at this time: Be sure to use remember for all variables.

Common mistakes are:


@Composable
fun SomeList() {
    val list = listOf("a", "b", "c")
    LazyColumn {
        items(list) {
            Text(it)
        }
    }
}

The list here is not remembered at all, and the Compose function will be executed very frequently, which leads to an assignment or even calculation every time val list = listOf("a", "b", "c") is executed The operation, this way of writing will greatly affect the performance, the correct way of writing should be like this:


@Composable
fun SomeList() {
    val list = remember { listOf("a", "b", "c") }
    LazyColumn {
        items(list) {
            Text(it)
        }
    }
}

Of course it is better to move the list to the parameter:


@Composable
fun SomeList(
    list: List<String>,
) {
    LazyColumn {
        items(list) {
            Text(it)
        }
    }
}

5. Parameters are mutable

Still following the previous example, it is not enough just to move the list to the parameter, because you can change the list outside the Composable function, such as executing list.add(""), the Compose compiler will think that this Composable function is still with state, so it is not yet the most optimized state. Better to use ImmutableList from kotlinx.collections.immutable:

@Composable
fun SomeList(
    list: ImmutableList<String>,
) {
    LazyColumn {
        items(list) {
            Text(it)
        }
    }
}

In addition to the basic type, the custom class in other parameters is best marked with @Immutable, so that the Compose compiler will optimize the Composable function. Of course, don’t define a data class and put a var a: String in it, and then ask why aa = “b” has no effect. It is recommended that the data class passed to the Composable function is all val.

6. R8 not turned on

R8 has greatly improved Compose. If it is a simple UI, it may still be usable without R8. It is highly recommended to enable R8 for a complex UI. After code optimization, the performance of Debug has a huge gap.

7. Finally: recommended interview questions

In fact, after understanding the core idea of ​​Compose UI, the Compose program written should not have any performance problems, and the logic of Compose UI written under this core idea is very clear, because the entire UI is stateless, you only need to In what state the relationship is displayed in this UI, the mental burden is very small.

Finally, I recommend some Compose-related interview questions. You can do a self-test. If you can answer seven or eight, congratulations, you may have defeated 95% of your peers.

  • Does Jetpack Compose understand? How is it different from traditional Android UI?
  • The difference between DisposableEffect, SideEffect, LaunchedEffect?
  • How are pointer events handled between Composable functions?
  • Custom Layout?
  • What does CompositionLocal do? What is the difference between staticCompositionLocalOf and compositionLocalOf?
  • How is the state of a Composable function persisted?
  • How does LazyColumn do Composable function caching?
  • How to solve the sliding conflict of LazyColumn and other Composable functions?
  • What is the role of @Composable?
  • What is Jetpack Compose used for rendering? What is the execution process like? What's the difference/pros and cons of doing diff like flutter/react does?
  • How is Jetpack Compose multi-threaded execution implemented?
  • What are stateful composable functions? What is a stateless Composable function?
  • How to understand the state promotion of Compose? what is the benefit?
  • How to understand the MVI architecture? How is it different from MVVM, MVP, MVC?
  • On Android, when a Flow is collectedAsState and the application is transferred to the background, if the Flow is updated again, will the corresponding State be updated? Will the corresponding Composable function be updated?

Having said so much, I didn't say how to learn. In the following Jetpack Compose development guide released by Google, you will learn:

  • Different migration paths you can follow
  • How to Migrate Your App to Compose Step by Step
  • How to add Compose to an existing interface built with View
  • How to use Views in Compose
  • How to use View-based themes in Compose
  • How to test a hybrid interface written with View and Compose

Android Jetpack Compose Development Application Guide

Chapter 1 Introduction to Jetpack

  • What is JetPack
  • JetPack and AndroidX
  • Migration of AndroidX

insert image description here

Chapter 2 Design Principles and Basic Concepts of Compose

  • JetPack Compose environment construction
  • JetPack Compose new features and component dependencies
  • Summary of JetPack Compose programming ideas

insert image description here

Chapter 3 Getting Started with Compose

  • Basic case for getting started with JetPack Compose
  • Basic case for getting started with JetPack Compose
    insert image description here

Chapter 4 Compose Layout

  • Compose State
  • Compose style (Theme)
  • Compose layout core controls
  • custom layout
  • ConstraintLayout in Compose

Chapter 5 Compose animation

  • Compose SideEffect
  • Compose animation overview
  • Compose Crossfade
  • Compose animateContentSize
  • Animatable
  • Compose custom animation

Chapter 6 Compose Graphics

  • Compose Canvas
  • Analysis of Compose drawing API
  • Compose custom drawing
    insert image description here

Chapter 7 Summary of Compose Core Controls

  • Scaffold
  • LazyColumn
    insert image description here

Friends in need can [click the card below] to download for free .

Guess you like

Origin blog.csdn.net/Eqiqi/article/details/132064429