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

Deep Dive into Jetpack Compose State

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →

Deep Dive into Jetpack Compose State

Ji Sungbin

May 22, 2023

Transcript

70 slides
  1. 2023 찰스의 안드로이드 컨퍼런스 목표 •스냅샷 시스템의 컨셉을 이해한다. •StateObject와

    StateRecord를 이해한다. •Undo/Redo 시스템을 만들 수 있다. •발표의 이해도를 높이기 위해 목표와 무관한 내용은 전체 생략
  2. 2023 찰스의 안드로이드 컨퍼런스 📸 Snapshot System •컴포즈에서 두 번째로

    깊은 계층 •State의 구현체 •리컴포지션의 뼈대
  3. 2023 찰스의 안드로이드 컨퍼런스 이걸 왜 알아야 해? •내부 개념인

    만큼 컴포즈를 사용한다고 알아야 할 필요는 없다. •컴포즈 런타임 구성으로만 쓰이기엔 아까운 기술이고, 대부분 public api로 
 구현돼 있어서 컴포즈 내부에 관심이 있다면 한 번쯤 봐볼 가치는 있다. •이해하고 있으면 컴포즈의 상태 시스템을 자유자제로 다룰 수 있다.
  4. 2023 찰스의 안드로이드 컨퍼런스 📸 Snapshot System 컨셉 컴포지션은 특정

    순서에 구애받지 않고 무작위 병렬로 실행된다. → 하나의 State에 여러 컴포지션이 동시에 접근할 수 있으며, 동시성 문제에 빠질 수 있음
  5. 2023 찰스의 안드로이드 컨퍼런스 📸 Snapshot System 컨셉 💡 모든

    State 연산을 고립되게 진행하자! 컴포지션은 특정 순서에 구애받지 않고 무작위 병렬로 실행된다. → 하나의 State에 여러 컴포지션이 동시에 접근할 수 있으며, 동시성 문제에 빠질 수 있음
  6. class StringPrinter { private var value: String? = null fun

    delayedPrint(value: String, delay: Long = 1000L) { this.value = value Thread.sleep(delay) println(this.value) } }
  7. fun main() { val stringPrinter = StringPrinter() thread { stringPrinter.delayedPrint("A")

    } thread { stringPrinter.delayedPrint("B") } } // result: B, B
  8. class StringPrinter { private val value = ThreadLocal<String>() fun delayedPrint(value:

    String, delay: Long = 1000L) { this.value.set(value) Thread.sleep(delay) println(this.value.get()) } } // result: A, B
  9. class StringPrinter { private val value = ThreadLocal<String>() fun delayedPrint(value:

    String, delay: Long = 1000L) { this.value.set(value) Thread.sleep(delay) println(this.value.get()) } } // result: A, B Snapshot
  10. class StringPrinter { private val value = ThreadLocal<String>() fun delayedPrint(value:

    String, delay: Long = 1000L) { this.value.set(value) Thread.sleep(delay) println(this.value.get()) } } // result: A, B Snapshot “Snapshot System”
  11. @Composable fun main(state: MutableState<Int>) { // 0 state.value = 1

    state.value = 2 // recomposition! } 0 📸 1 2
  12. @Composable fun main(state: MutableState<Int>) { // 0 -> 10 state.value

    = 1 state.value = 2 
 // ৻ࠗ੄ ৔ೱਵ۽ state ਗࠄ ч੉ // زदী ߄Պ } 0 📸 1 2 10
  13. @Composable fun main(state: MutableState<Int>) { // 0 -> 10 state.value

    = 1 state.value = 2 
 // recomposition! } 0 📸 1 2 10
  14. class StringPrinter { private val value = ThreadLocal<String>() fun delayedPrint(value:

    String, delay: Long = 1000L) { this.value.set(value) Thread.sleep(delay) println(this.value.get()) } } ` StateObject StateRecord
  15. val state = mutableStateOf(0) state.value = 1 state.value = 2

    state.value = 3 ` StateObject StateRecord
  16. StateRecord // mutableೠ о੢ ୭न ۨ௏٘ী ੘স ࣻ೯ fun <R>

    writable( state: StateObject, block: StateRecord.() -> R, ): R
  17. 2023 찰스의 안드로이드 컨퍼런스 Undo/Redo 시스템 1. 상태가 변경될 때마다

    새로운 상태 값 저장 2. Undo 요청 시 이전 값으로 상태 복원 3. Redo 요청 시 다음 값으로 상태 복원
  18. // അ੤ ೐ۨ੐ ੋؙझ var currentFrame = 0 // пп

    ೐ۨ੐߹ ࢚క ч(StateRecord) val frames = mutableListOf<StateRecord>()
  19. // ୶੸ ઺ੋ ࢚క੄ ч(StateRecord)ਸ ೐ۨ੐ ߓৌী ੷੢ fun saveFrame()

    { frames += target!!.copyCurrentRecord() currentFrame++ }
  20. // ઱য૓ ࢚కী ೡ׼ػ ч(StateRecord)ਸ ࠂࢎೞৈ ߈ജ fun StateObject.copyCurrentRecord(): StateRecord

    { val newRecord = firstStateRecord.create() firstStateRecord.withCurrent { current -> newRecord.assign(current) } return newRecord }
  21. // ઱য૓ State੄ ࢚క ߸҃ਸ ୶੸ೞب۾ ૑੿ fun <T> MutableState<T>.track():

    MutableState<T> { target = this as StateObject return this }
  22. 2023 찰스의 안드로이드 컨퍼런스 Undo/Redo 시스템 1. 상태가 변경될 때마다

    새로운 상태 값 저장 2. Undo 요청 시 이전 값으로 상태 복원 3. Redo 요청 시 다음 값으로 상태 복원
  23. // ୶੸ ઺ੋ ࢚క੄ чਸ ੉੹ ೐ۨ੐ਵ۽ ࠂਗ fun undo()

    { if (currentFrame - 1 in frames.indices) { target!!.restoreFrom(frames[--currentFrame]) } }
  24. // ୶੸ ઺ੋ ࢚క੄ чਸ ׮਺ ೐ۨ੐ਵ۽ ࠂਗ fun redo()

    { if (currentFrame + 1 in frames.indices) { target!!.restoreFrom(frames[++currentFrame]) } }
  25. 2023 찰스의 안드로이드 컨퍼런스 Undo/Redo 시스템 1. 상태가 변경될 때마다

    새로운 상태 값 저장 2. Undo 요청 시 이전 값으로 상태 복원 3. Redo 요청 시 다음 값으로 상태 복원
  26. 2023 찰스의 안드로이드 컨퍼런스 지금까지 만든 Undo/Redo 시스템 1. saveFrame():

    현재 프레임의 상태 값(StateRecord) 저장 2. track(): 추적할 상태(StateObject) 지정 3. undo(): 이전 프레임으로 상태 값(StateRecord) 복원 4. redo(): 다음 프레임으로 상태 값(StateRecord) 복원
  27. 2023 찰스의 안드로이드 컨퍼런스 이제 만들 Undo/Redo 시스템 1. saveFrame()

    호출 시점 2. 과거 프레임에서 신규 프레임을 저장했을 때 프레임 처리 정책
  28. 2023 찰스의 안드로이드 컨퍼런스 saveFrame() 호출 시점 • 추적 중인

    상태(StateObject)에 값(StateRecord)이 작성됐을 때 🙆 • undo()와 redo()로 상태 값(StateRecord)이 복원됐을 때 🙅
  29. // StateObjectী StateRecordо ੘ࢿؼ ٸ ഐ୹غח ௒ߔ ١۾ // Set<Any>:

    ੘ࢿػ StateObject ݽ਺ context(Snapshot) fun registerApplyObserver( observer: (Set<Any>, Snapshot) -> Unit, ): ObserverHandle
  30. // ୶੸ ઺ੋ ࢚కо ߸ೡ ٸ݃׮ saveFrame() ഐ୹ द੘ fun

    startRecording() { handle = Snapshot.registerApplyObserver { states, _ -> if (states.any { it == target }) { saveFrame() } } }
  31. // ઱য૓ State੄ ࢚క ߸҃ਸ ୶੸ೞب۾ ૑੿ fun <T> MutableState<T>.track():

    MutableState<T> { target = this as StateObject startRecording() return this }
  32. 2023 찰스의 안드로이드 컨퍼런스 saveFrame() 호출 시점 • 추적 중인

    상태(StateObject)에 값(StateRecord)이 작성됐을 때 🙆 • undo()와 redo()로 상태 값(StateRecord)이 복원됐을 때 🙅
  33. // ୶੸ ઺ੋ ࢚క੄ чਸ ੉੹ ೐ۨ੐ਵ۽ ࠂਗ fun undo()

    { stopRecording() if (currentFrame - 1 in frames.indices) { target!!.restoreFrom(frames[--currentFrame]) } startRecording() }
  34. // ୶੸ ઺ੋ ࢚క੄ чਸ ׮਺ ೐ۨ੐ਵ۽ ࠂਗ fun redo()

    { stopRecording() if (currentFrame + 1 in frames.indices) { target!!.restoreFrom(frames[++currentFrame]) } startRecording() }
  35. 2023 찰스의 안드로이드 컨퍼런스 이제 만들 Undo/Redo 시스템 1. saveFrame()

    호출 시점 2. 과거 프레임에서 신규 프레임을 저장했을 때 프레임 처리 정책
  36. 2023 찰스의 안드로이드 컨퍼런스 프레임 처리 정책 과거 프레임에서 신규

    프레임을 저장하면 
 과거와 현재 사이의 모든 프레임 제거 후 신규 프레임 저장
  37. // ୶੸ ઺ੋ ࢚క੄ ч(StateRecord)ਸ ೐ۨ੐ ݾ۾ী ੷੢ fun saveFrame()

    { if (currentFrame < frames.lastIndex) { frames.removeRange(start = currentFrame + 1, 
 end = frames.size) } frames += target!!.copyCurrentRecord() currentFrame++ }
  38. var currentFrame = 0 val frames = mutableListOf<StateRecord>() var target:

    StateObject? = null var handle: ObserverHandle? = null
  39. fun saveFrame() { if (currentFrame < frames.lastIndex) { frames.removeRange(currentFrame +

    1, frames.size) } frames += target!!.copyCurrentRecord() currentFrame++ }
  40. fun startRecording() { handle = Snapshot.registerApplyObserver { states, _ ->

    if (states.any { it == target }) { saveFrame() } } }
  41. fun undo() { stopRecording() if (currentFrame - 1 in frames.indices)

    { target!!.restoreFrom(frames[--currentFrame]) } startRecording() }
  42. fun redo() { stopRecording() if (currentFrame + 1 in frames.indices)

    { target!!.restoreFrom(frames[++currentFrame]) } startRecording() }
  43. // duckie-team/quack-quack-android Text( modifier = Modifier.span( texts = listOf("QuackQuack"), style

    = SpanStyle(color = Orange), ), text = "QuackQuack is an awesome ui kit.", typography = Body1, )

More Decks by Ji Sungbin

Other Decks in Technology