Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Connect Recharge: What does Recomposition mean to your app?

Connect Recharge: What does Recomposition mean to your app?

LITE version

PRESENTED AT:
Connect Recharge 2022

https://hopin.com/events/connect-recharge-2022/registration?code=wwK1JKQKXBSzI6RaQZcehxqtr

DESCRIPTION:
You’ve heard a lot that Jetpack Compose is a declarative UI toolkit and it recomposes only the components that changed. But what does it exactly mean? How does it apply not only in the scale of one composable function but the entire app? Well, it turns out there is more than meets the eye when it comes to recomposition, and in this talk, we will deep dive into its benefits, costs, and gotchas. You’ll learn some practical tips to efficiently build functions in Jetpack Compose.

MORE TALKS & ARTICLES FROM ME: https://cupsofcode.com/talks/

Aida Issayeva

May 26, 2022
Tweet

More Decks by Aida Issayeva

Other Decks in Programming

Transcript

  1. About me 🤖 Android GDE 󰠁 Sr. Software Engineer 󰠅

    Instructor 🐦 @aida_isay 🌐 cupsofcode.com 📽 @aida_isay
  2. TODAY’S AGENDA 02 Smart Recomposition 03 Tools to detect &

    debug 04 Practical tips 05 01 What is Recomposition? Jetpack Compose Paradigm
  3. Jetpack Compose paradigm UI widgets are functions They are stateless

    Different arguments, passed to the functions, trigger updates
  4. 01 Jetpack Compose Paradigm Compose Phases 1 Composition Calls composable

    functions and creates a description of the UI 2 Layout Measures the layout elements and their children and places them on the screen 3 Drawing Draws the UI elements to the screen
  5. Recomposition in SlotTable: example 1 @Composable fun Card() { val

    counter = remember { mutableStateOf(0) } Column { Text(text = "Compose") Button(onClick = { counter.value++ }) { Text(text = "Click me $counter") } } } 02 What is recomposition Group(456) ….. Group(789) “Compose” Group(101) “Click me 0” C A R D C O L U M N B U T T O N { ... } Group(123) State(0) R E M E M B E R
  6. Recomposition in SlotTable: example 1 @Composable fun Card() { val

    counter = remember { mutableStateOf(0) } Column { Text(text = "Compose") Button(onClick = { counter.value++ }) { Text(text = "Click me $counter") } } } 02 What is recomposition Group(456) ….. Group(789) “Compose” Group(101) “Click me 0” C A R D C O L U M N B U T T O N { ... } Group(123) State(0) R E M E M B E R
  7. Recomposition in SlotTable: example 1 @Composable fun Card() { val

    counter = remember { mutableStateOf(0) } Column { Text(text = "Compose") Button(onClick = { counter.value++ }) { Text(text = "Click me $counter") } } } 02 What is recomposition Group(456) ….. Group(789) “Compose” Group(101) “Click me 1” C A R D C O L U M N B U T T O N { ... } Group(123) State(1) R E M E M B E R
  8. Recomposition in SlotTable: example 2 @Composable fun Card() { val

    counter = remember { mutableStateOf(0) } Column { Text(text = "Compose $counter") Button(onClick = { counter.value++ }) { Text(text = "Click me") } } } 02 What is recomposition Group(456) ….. Group(789) “Compose 0” Group(101) “Click me” C A R D C O L U M N B U T T O N { ... } Group(123) State(0) R E M E M B E R
  9. Recomposition in SlotTable: example 2 @Composable fun Card() { val

    counter = remember { mutableStateOf(0) } Column { Text(text = "Compose $counter") Button(onClick = { counter.value++ }) { Text(text = "Click me") } } } 02 What is recomposition Group(456) ….. Group(789) “Compose 1” Group(101) “Click me” C A R D C O L U M N B U T T O N { ... } Group(123) State(0) R E M E M B E R
  10. 02 What is recomposition Compose Phases 1 Composition Calls composable

    functions and creates a description of the UI 2 Layout Measures the layout elements and their children and places them on the screen 3 Drawing Draws the UI elements to the screen
  11. Inputs are stable Smart recomposition is an intelligent way to

    skip the recomposition of composable functions when their
  12. Stable type contract rules: RULE 1 The result of calls

    for two instances to `equals()` function will always be the same for the same two instances RULE 2 When a public property of the type changes, Composition is notified RULE 3 All public properties of the type are also stable
  13. Exceptions: 1. Enum 2. Enum Entry 3. Interface 4. Annotation

    5. Companion Object 6. Inline 7. Anonymous object 8. Expect element 9. Inner class 03 Smart Recomposition Inferring Class stability • visits each eligible class • annotates with @StabilityInferred • helps compiler to determine what to recompose
  14. 1. Recompose Highlighter ➢ Google Play team introduced the modifier

    ➢ Use this link below to check out the code snippet 04 Tools to detect
  15. 2. Log Statements 04 Tools to detect ➢ Ancient and

    the most reliable. ➢ Android Studio Electric Eel has an automatic support for it. ➢ Wrapped as a modifier function. Check out the code snippet below
  16. 3. Compose Compiler Metrics 04 Tools to detect { "skippableComposables"

    : 64, "restartableComposables" : 76, "readonlyComposables" : 0, "totalComposables" : 76 } ➢ Full analysis ➢ Restartable & !skippable = ⛳ ➢ Check out the code snippet below for integration details
  17. 1. Break down composable functions /** * 👍 : only

    associated column with changes gets recomposed. */ @Composable fun GoodGreetingsTip1() { Column { Greeting("Android", 1, Gray200) Greeting("Compose", 2, Brown200) } } @Composable fun Greeting(name: String, columnNumber: Int, background: Color) { Column { . . . } } 05 Practical tips
  18. 2. Use the key composable /** * 👍 : the

    key is set, and when item in the middle is removed, only LazyColumn gets recomposed * and not its children */ @Composable fun GoodGreetingsTip2(fruits: List<String>) { LazyColumn( modifier = Modifier .recompositionCounter("lazyColumn") ) { items( items = fruits, key = { it } ) { fruit -> Item(fruit) } } } 05 Practical tips
  19. 3. Read the state value at the lowest composable function

    /** * 👍 : only inner column and its children get recomposed, because the read happens * in one of the children. */ @Composable fun GoodGreetingsTip3(cardInfo: () -> CardInfo) { Column { UpperRow() GoodRow(cardInfo = cardInfo) } } @Composable private fun GoodRow(cardInfo: () -> CardInfo) { Row { GoodInnerColumn(cardInfo = cardInfo) } } 05 Practical tips
  20. 4. Use modifier lambdas /** * 👍: With the receiver

    lambda function (Modifier.offset{}) in place, this composable function doesn't get recomposed. */ @Composable private fun GoodGreetingsTip4(yAxis: () -> Int) { Row( modifier = Modifier .offset { IntOffset(y = yAxis(), x = 0) }, verticalAlignment = Alignment.Bottom ) { . . . } } 05 Practical tips
  21. IMPORTANT 1 Move any logic out of the composable functions

    to viewmodels, presenters, etc 5. Tips worth re-repeating IMPORTANT 2 Don’t create dependable composable functions on the result of other composables functions IMPORTANT 3 Avoid any side-effects in the composable functions, because they can run in parallel
  22. Resources 1. Jetpack Compose Internals 2. Jetpack Compose source code

    3. Understanding Jetpack Compose — part 1 of 2 4. Understanding Jetpack Compose — part 2 of 2 5. Recomposition made easy 6. RecomposeHighlighter code snippet 7. Compose Compiler Metrics 8. A historical introduction to the Compose reactive state model 9. What is “donut-hole” skipping in Jetpack Compose? 10. Compose performance 11. Composable Metrics 12. Thinking in Compose 13. Jetpack Compose Phases 14. Jetpack Compose Lifecycle Resources Recomposition tips code samples