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

[DC SF 2022] Hitchhiking through Jetpack Compose

[DC SF 2022] Hitchhiking through Jetpack Compose

Avatar for Jossi Wolf

Jossi Wolf

June 02, 2022
Tweet

More Decks by Jossi Wolf

Other Decks in Programming

Transcript

  1. Hitchhiking through Jetpack Compose Amanda Hinchman-Dominguez Kotlin GDE, Android Engineer

    @ Groupon Jossi Wolf Software Engineer @ Google #hg2compose
  2. @Composable fun MyApp() { var counter by remember { mutableStateOf(0)

    } HelloWorld(“Clicked $counter times”) Button(onClick = { counter.+ }) { … } } @Composable fun HelloWorld(greeting: String) { Text(“Hello World, $greeting”) } @mvndy_hd @jossiwolf #hg2compose
  3. "A Snapshot is a lot like a save point in

    a video game: it represents the state of your entire program at a single point in history." - "Introduction to the Compose Snapshot System" by Zach Klippenstein Snapshots 📸 @mvndy_hd @jossiwolf #hg2compose
  4. 1 Snapshots 📸 data class Dog( val name: MutableState<String> )

    Dog(name = "Spot") @mvndy_hd @jossiwolf #hg2compose
  5. 1 2 Snapshots 📸 📸 Dog(name = "Spot") “Spot” dog.name.value

    = "Fido" Dog(name = "Fido") @mvndy_hd @jossiwolf #hg2compose
  6. 1 2 Snapshots 📸 📸 Dog(name = "Spot") “Spot” dog.name.value

    = "Fido" Dog(name = "Fido") snapshot.enter { dog.name.value = "Taco" } @mvndy_hd @jossiwolf #hg2compose
  7. 1 2 Snapshots 📸 📸 Dog(name = "Spot") “Spot” dog.name.value

    = "Fido" Dog(name = "Fido") snapshot.enter { dog.name.value = "Taco" } Dog(name = "Taco") @mvndy_hd @jossiwolf #hg2compose
  8. 1 2 Snapshots 📸 📸 Dog(name = "Spot") “Spot” dog.name.value

    = "Fido" Dog(name = "Fido") snapshot.enter { dog.name.value = "Taco" } Dog(name = "Taco") snapshot.apply() “Taco” @mvndy_hd @jossiwolf #hg2compose
  9. - State changes cause recomposition - Compose has a Snapshot

    system Recap! @mvndy_hd @jossiwolf #hg2compose
  10. https://dev.to/zachklipp/introduction-to-the-compose-snapshot-system-19cn fun main() { val dog = Dog() dog.name.value =

    "Spot" val snapshot = Snapshot.takeSnapshot( readObserver = { readObject .> } ) snapshot.enter { println(dog.name.value) } } Snapshots 📸 @mvndy_hd @jossiwolf #hg2compose
  11. #hg2compose fun main() { val dog = Dog() dog.name.value =

    "Spot" val snapshot = Snapshot.takeSnapshot( readObserver = { readObject .> ./ MutableState(value = "Spot") } ) snapshot.enter { println(dog.name.value) } } Snapshots 📸 @mvndy_hd @jossiwolf https://dev.to/zachklipp/introduction-to-the-compose-snapshot-system-19cn
  12. @Composable fun HelloWorld(greeting: String, %composer: Composer?, %changed: Int) { %composer

    = %composer.startRestartGroup(.>) val %dirty = %changed if (%changed and 0b1110 ..= 0) { %dirty = %dirty or if (%composer.changed(greeting)) 0b0100 else 0b0010 } if (%dirty and 0b1011 xor 0b0010 ..= 0 .| !%composer.skipping) { Text("Hello, $greeting!") } else { %composer.skipToGroupEnd() } %composer.endRestartGroup() ..updateScope { %composer: Composer?, %force: Int .> HelloWorld(greeting, %composer, %changed or 0b0001) } } #hg2compose @mvndy_hd @jossiwolf
  13. @Composable fun HelloWorld(names: List<String>) { names.forEach { name .> key(name)

    { Text(name) } } } Moveable Groups @mvndy_hd @jossiwolf #hg2compose
  14. @Composable fun HelloWorld(name: String) { if (name .= "Amanda") {

    Text("Hello!") else if (name .= "Jossi") { Text("Hallo!") } else { Text("Uhm.. hi?") } } Replaceable Groups @mvndy_hd @jossiwolf #hg2compose
  15. .kt Lexer Analysis Syntax Analysis Parsing Phase Generates tokens Kotlin

    Compiler Frontend Build PSI tree Add info to nodes Analysis Resolution Phase Generate descriptors for PSI + Symbol Tables + + Enhance PSI Elements w/ Descriptors + Symbol table maps PSI to descriptor + IR calls Run static analysis, type verification, diagnostics Resolve + + @mvndy_hd @jossiwolf #hg2compose
  16. #hg2compose - Performs optimisations on IR - Removes dead code

    - Refactors the code - Improves performance - Analyzes IR for data needed to create - Call graph - Control-flow graph CPU-related IR Transformations IR Transform Compiler Analysis Middle End “Lower” Backend - Takes optimized IR and performs more analysis, transformations + optimisations specific to target CPU architecture - Multithreading < > Bytecode Target Program - Performs optimisations on IR - Removes dead code - Refactors the code - Improves performance - Analyzes IR for data needed to create - Call graph - Control-flow graph CPU-related IR Transformations FUNC VAL_PARAM VAL_PARAM VA:_PARAM Unoptimized IR @mvndy_hd @jossiwolf
  17. @Composable fun HelloWorld(greeting: String, %composer: Composer?, %changed: Int) { %composer

    = %composer.startRestartGroup(.>) val %dirty = %changed if (%changed and 0b1110 ..= 0) { %dirty = %dirty or if (%composer.changed(greeting)) 0b0100 else 0b0010 } if (%dirty and 0b1011 xor 0b0010 ..= 0 .| !%composer.skipping) { Text("Hello, $greeting!") } else { %composer.skipToGroupEnd() } %composer.endRestartGroup() ..updateScope { %composer: Composer?, %force: Int .> HelloWorld(greeting, %composer, %changed or 0b0001) } } #hg2compose @mvndy_hd @jossiwolf
  18. var name by remember { mutableStateOf("Bob") } @Composable fun Greeting(greeting:

    String) { Text("Hello, $greeting!") } Greeting(name) name = "Jossi" ./ Hello, Bob! ./ Hello, Jossi! @mvndy_hd @jossiwolf Slot Table by Example #hg2compose
  19. EMPTY EMPTY Group(4) State(“Bob”) var name by remember { mutableStateOf("Bob")

    } EMPTY EMPTY EMPTY EMPTY EMPTY @mvndy_hd @jossiwolf Slot Table by Example #hg2compose
  20. Group(4) State(“Bob”) Group(5) var name by remember { mutableStateOf("Bob") }

    EMPTY EMPTY EMPTY EMPTY EMPTY EMPTY @Composable fun Greeting(greeting: String) { } Greeting(name) @mvndy_hd @jossiwolf Slot Table by Example #hg2compose
  21. EMPTY EMPTY Group(4) State(“Bob”) Group(5) Group(6) Group(7) Group(8) “Hello, Bob!”

    var name by remember { mutableStateOf("Bob") } @Composable fun Greeting(greeting: String) { Text("Hello, $greeting!”) } Greeting(name) @mvndy_hd @jossiwolf Slot Table by Example #hg2compose
  22. EMPTY EMPTY Group(4) State(“Bob”) Group(5) Group(6) Group(7) Group(8) “Hello, Jossi!”

    var name by remember { mutableStateOf("Bob") } @Composable fun Greeting(greeting: String) { Text("Hello, $greeting!”) } Greeting(name) name = "Jossi" @mvndy_hd @jossiwolf Slot Table by Example #hg2compose
  23. interface Applier<N> { val current: N fun onBeginChanges() {} fun

    onEndChanges() {} fun down(node: N) fun up() fun insertTopDown(index: Int, instance: N) fun insertBottomUp(index: Int, instance: N) fun remove(index: Int, count: Int) fun move(from: Int, to: Int, count: Int) fun clear() } The Applier @mvndy_hd @jossiwolf #hg2compose
  24. interface Applier<N> { val current: N fun onBeginChanges() {} fun

    onEndChanges() {} fun down(node: N) fun up() fun insertTopDown(index: Int, instance: N) fun insertBottomUp(index: Int, instance: N) fun remove(index: Int, count: Int) fun move(from: Int, to: Int, count: Int) fun clear() } The Applier @mvndy_hd @jossiwolf #hg2compose
  25. LayoutNode! ..and then what? @mvndy_hd @jossiwolf #hg2compose class AndroidComposeView(context: Context)

    : ViewGroup(context), ... { override fun dispatchDraw(canvas: Canvas) { ... measureAndLayout() ... } }
  26. • Jorge Castillo, author of "Compose Internals" • Leland Richardson

    for his work and sitting down with us to interview him • Zach Klippenstein's 5-part series on Compose state explained • George Mount for his explanation of Compose UI fundamentals • Matvei Malkov, Simona Stojanovic for reviews! #hg2compose @mvndy_hd @jossiwolf Thank you!!