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

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!!