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

DC SF - Hitchhiking Through Jetpack Compose

Jossi Wolf
September 01, 2022

DC SF - Hitchhiking Through Jetpack Compose

Jossi Wolf

September 01, 2022
Tweet

More Decks by Jossi Wolf

Other Decks in Programming

Transcript

  1. @mvndy_hd @jossiwolf #hg2compose @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”) }
  2. "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
  3. 1 Snapshots 📸 data class Dog( val name: MutableState<String> )

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

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

    = "Fido" Dog(name = "Fido") @mvndy_hd @jossiwolf #hg2compose println(dog.name.value) // Fido
  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 - Snapshots power state 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. #hg2compose @mvndy_hd @jossiwolf The Kotlin Compiler .kt Lexer Analysis Syntax

    Analysis Generates tokens 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 + + Parsing Phase + + + + + + • A compiler does two things with data: ◦ Compiles - changes format ◦ Lowers - simplifies data IR Transformations Optimizing Phase < > Bytecode Codegen Phase Analyzes IR data to create additional logic - Call graph - Control-flow graph CPU-related IR Transformations FUNC VAL_PARAM VAL_PARAM VA:_PARAM Unoptimized IR - Performs additional analysis, transformations + optimisations specific to target CPU architecture - Analyzes IR optimized data to create CPU-related logic i.e. multithreading IR-to-Bytecode Generation Perform optimizations on IR - Remove dead code - Refactor code - Improve performance Compiler Analysis Target Program
  14. #hg2compose @mvndy_hd @jossiwolf .kt Lexer Analysis Syntax Analysis Generates tokens

    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 + + Parsing Phase + + + + + + • A compiler does two things with data: ◦ Compiles - changes format ◦ Lowers - simplifies data • Compiler plugins can intercept compiler behavior at any phase The Kotlin Compiler IR Transformations Optimizing Phase < > Bytecode Codegen Phase Analyzes IR data to create additional logic - Call graph - Control-flow graph CPU-related IR Transformations FUNC VAL_PARAM VAL_PARAM VA:_PARAM Unoptimized IR - Performs additional analysis, transformations + optimisations specific to target CPU architecture - Analyzes IR optimized data to create CPU-related logic i.e. multithreading IR-to-Bytecode Generation Perform optimizations on IR - Remove dead code - Refactor code - Improve performance Compiler Analysis Target Program
  15. .kt Lexer Analysis Syntax Analysis 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 + + #hg2compose Parsing Phase + + + + + + @mvndy_hd @jossiwolf
  16. #hg2compose IR Transformations Optimizing Phase < > Bytecode Codegen Phase

    Analyzes IR data to create additional logic - Call graph - Control-flow graph CPU-related IR Transformations FUNC VAL_PARAM VAL_PARAM VA:_PARAM Unoptimized IR - Performs additional analysis, transformations + optimisations specific to target CPU architecture - Analyzes IR optimized data to create CPU-related logic i.e. multithreading IR-to-Bytecode Generation Perform optimizations on IR - Remove dead code - Refactor code - Improve performance Compiler Analysis Target Program Kotlin Compiler Backend
  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. Slot Table & Trees @mvndy_hd @jossiwolf #hg2compose Composition Slot Table

    Applier Virtual representation of the composition
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. Layout & Draw @mvndy_hd @jossiwolf #hg2compose class AndroidComposeView(context: Context) :

    ViewGroup(context), ... { override fun dispatchDraw(canvas: Canvas) { } }
  25. • Leland Richardson for his work and sitting down with

    us to interview him • Zach Klippenstein's 5-part series on Snapshot State • George Mount for his explanation of Compose UI fundamentals • Matvei Malkov, Simona Stojanovic for reviews! • Jorge Castillo, author of "Compose Internals" n n #hg2compose @mvndy_hd @jossiwolf Thank you!!
  26. Layout & Draw @mvndy_hd @jossiwolf #hg2compose AndroidComposeView Compose UI measureAndLayout()

    Android Framework dispatchDraw() Platform-agnostic Platform-independent
  27. @Composable fun HelloWorld(names: List<String>) { names.forEach { name .> key(name)

    { Text(name) } } } Moveable Groups @mvndy_hd @jossiwolf #hg2compose
  28. @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