Table Of Contents:
This post is extracted from my talk “What does recomposition mean to your app” .
What is recomposition
Recomposition is the action of forming something again or differently. When the term is used in Jetpack Compose, it means the action of executing the function again.
Jetpack Compose is a declarative UI toolkit in Android. The traditional XML-based style was imperative. The declarative style of building UI is focused on what to do, rather than how to do. It focuses on evaluating results based on input, rather than defining variables and changing their values.
Because Compose evaluates results based on the input of a function, a new input would trigger the recomposition that re-executes the function again.
Why do you need to be concerned about recomposition?
When building a feature in Jetpack Compose, you might notice a drop in your UI performance: some sort of jitteriness, lag, unexpected behavior, etc.
This happens because of improper implementation of composable functions. On one hand, it’s forcing your UI to be recomposed excessively and do more work than necessary, thus directly affecting the performance of your app. On the other hand, it doesn’t update your UI when you expect it to recompose, thus leading to unexpected behavior and bugs in your app.
When Compose updates a frame, it goes through three phases: Composition, Layout and Drawing. During these phases, Compose runs composable functions, builds the UI tree, determines size and placement of each element in the tree, and then renders each UI element.
It also has a built-in algorithm, smart recomposition, that can intelligently skip some of these tasks if they aren’t needed. However, improper implementation of composable functions makes it harder for Compose to know which tasks to skip, thus the recomposition can trigger these tasks all over again. It is potentially expensive in terms of time, computing power, and battery usage of a device.
In both cases, having a good set of tools, that will help you find issues, understand recomposition better, and learn the best practices of implementing composable functions, is beneficial for you to move through the learning curve of Jetpack Compose framework.
Tools to detect recomposition
I’ve compiled a list of tools that will help you to detect and debug recomposition in your app.
- Compose compiler metrics
Compiler metrics are reports about compose-specific concepts generated by Compose Compiler to understand what is happening with the code on fined-grain level.
Pros | Cons |
---|---|
1. Full-analysis including info about not skippable and restartable functions | 1. Should be completed on release build, which might not be ideal when building a feature |
To lean more and enable the metrics, check out the post from Chris Banes on compiler metrics .
- Recompose Highlighter modifier from Google Play team
Pros | Cons |
---|---|
1. visually appealing | 1. in nested functions, it’s difficult to separate and know what function is recomposed |
2. labor consuming - have to add to each modifier function |
- Log statements
You might have not expected to see log statements in the list, however it is ancient way of debugging and even it can help you to debug recomposition, although it can get noisy and be difficult to filter, especially if you have a lot of functions. If your goal of logging is not to track recomposition, then use DisposableEffect
or LaunchEffect
.
Let’s overview some of the ways of placing log statements.
- Adding directly in function
1@Composable
2fun Example() {
3 println("Example fun")
4 Column {
5 }
6}
- Putting behind
SideEffect
1@Composable
2fun Example() {
3 SideEffect {
4 println("Example fun")
5 }
6 Column {
7 }
8}
- Making a separate modifier , that counts number of recompositions.
1@Composable
2fun Example() {
3 Column(
4 modifier = Modifier.recompositionCounter("column in Example")
5 ) {
6 }
7}
Strategy | Pros | Cons |
---|---|---|
1. Adding directly in function | easy to add | logging happens even if recomposition failed or canceled → in this case it’s better to leverage SideEffect |
2. Putting behind SideEffect | runs after successful composition | won’t trigger recomposition if you’re performing snapshot state reads in the log statement |
3. Making a separate modifier | runs after successful composition | labor consuming - have to add to each modifier function |
- Android Studio Electric Eel with recomposition counter
In Android Studio Electric Eel you can use the layout inspector to check how often a composable is recomposed or skipped .
Pros | Cons |
---|---|
1. shows number of compositions & number of skips | 1. it’s in canary build, so you have to accept the potential issues with the IDE |
2. no need to manually add anything to the code | 2. you have to be in minimum compose
version of 1.2.0-alpha03 in order to use the feature |
Final Thoughts
In this article we learned about the importance of recomposition and its effect on the performance of the app. Currently there are four of ways of detecting and debugging recomposition:
- Compose compiler metrics
- Recompose highlighter modifier
- Log statements
- Android Studio Electric Eel
Let me know in comments if there are other ways to debug recomposition. I would love to learn more!