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

Migrating to Kotlin State & Shared Flows

Migrating to Kotlin State & Shared Flows

B3f560d34c14a9113e5024bc34ac26a0?s=128

Mohit S

June 03, 2022
Tweet

More Decks by Mohit S

Other Decks in Programming

Transcript

  1. Mohit Sarveiya Migrating to Kotlin State & Shared Flows @heyitsmohit

  2. Migrating to Kotlin State & Shared Flows • State &

    Shared Flow APIs • Migrating from broadcast channels • Flow Sharing Strategies • Manage Backpressure
  3. Cold vs Hot Flows

  4. What is a cold stream? • Flow completes • Triggers

    same code for every new subscriber
  5. Cold Flows val flow = flowOf(1, 2, 3) 
 .map

    { it + 1 }
  6. Cold Flows val flow = flowOf(1, 2, 3) 
 .map

    { it + 1 } flow.collect { ... }
  7. Cold Flows val flow = flowOf(1, 2, 3) 
 .map

    { it + 1 } flow.collect { ... }
  8. Cold Flows val flow = flowOf(1, 2, 3) 
 .map

    { it + 1 } flow.collect { ... }
  9. Cold Flows val flow = flowOf(1, 2, 3) 
 .map

    { it + 1 } flow.collect { // 2, 3, 4 }
  10. Cold Flows val flow = flowOf(1, 2, 3) 
 .map

    { it + 1 } flow.collect { ... } flow.collect { ... }
  11. Cold Flows val flow = flowOf(1, 2, 3) 
 .map

    { it + 1 } flow.collect { ... } flow.collect { ... }
  12. Cold Flows val flow = flowOf(1, 2, 3) 
 .map

    { it + 1 } flow.collect { ... } flow.collect { ... }
  13. Cold Flows val flow = flowOf(1, 2, 3) 
 .map

    { it + 1 } flow.collect { ... } flow.collect { // 2, 3, 4 }
  14. What is a hot stream? • Never completes normally •

    Exists independently of subscribers.
  15. Hot Streams State Flow Shared Flow

  16. State Flow • Different ways to create state flows •

    Emit, collect • Conflation
  17. State Flow View View Model

  18. State Flow View View Model State

  19. State Flow sealed class UiState { 
 object Loading: UiState()

    
 data class Success(…): UiState() data class Error(…): UiState() }
  20. State Flow sealed class UiState { 
 object Loading: UiState()

    
 data class Success(…): UiState() data class Error(…): UiState() }
  21. State Flow sealed class UiState { 
 object Loading: UiState()

    
 data class Success(…): UiState() data class Error(…): UiState() }
  22. State Flow val stateFlow = MutableStateFlow()

  23. val stateFlow = MutableStateFlow( UiState.Loading ) State Flow

  24. State Flow val stateFlow = MutableStateFlow( . .. ) stateFlow.emit(

    
 UIState.Success( ... ) 
 )
  25. State Flow val stateFlow = MutableStateFlow( . .. ) stateFlow.value

    = UIState.Success( ... ) 

  26. State Flow val stateFlow = MutableStateFlow( . .. ) stateFlow.update

    { UIState.Success( .. . ) }
  27. State Flow val stateFlow = MutableStateFlow( . .. ) stateFlow.collect

    { .. . } 
 
 stateFlow.collect { .. . } Latest value is received
  28. State Flow Conflation val stateFlow = MutableStateFlow( . .. )

    stateFlow.value = UIState.Success( ... ) 
 
 stateFlow.value = UIState.Error( .. . ) Conflate
  29. State Flow Conflation val stateFlow = MutableStateFlow( . .. )

    stateFlow.value = UIState.Success( ... ) 
 
 stateFlow.value = UIState.Error( .. . ) 
 
 stateFlow.collect { .. . } Error
  30. State Flow View View Model State

  31. State Flow State Flow Flow 1 Flow 2 Flow 3

    Combined Flow
  32. Flow Combine 1 A 1A Flow 1 Flow 2 Combined

  33. Flow Combine 1 2 A 1A 2A Flow 1 Flow

    2 Combined
  34. Flow Combine 1 2 A 1A 2A Flow 1 Flow

    2 Combined B 2B
  35. val stateFlow = MutableStateFlow(UIState.Loading) 
 flow1.combine(flow2) { a, b -

    > } State Flow
  36. val stateFlow = MutableStateFlow(UIState.Loading) 
 flow1.combine(flow2) { a, b -

    > combineItems(a, b) } State Flow
  37. val stateFlow = MutableStateFlow(UIState.Loading) 
 flow1.combine(flow2) { a, b -

    > combineItems(a, b) }.collect { stateFlow.emit(it) } State Flow
  38. Flow Marbles

  39. State Flow View View Model State

  40. Cash App - Molecule

  41. Molecule Purpose 
 Build a StateFlow using Jetpack Compose

  42. Approach View Molecule Presenter 
 (Composable) Events

  43. Presenter @Composable 
 fun Presenter(eventFlow: Flow<Event>): UiState { 
 val

    event by eventFlow.collectAsState(null) return if (event = = null) { UiState.Loading } else { UiState.Data(…) } }
  44. Presenter @Composable 
 fun Presenter(eventFlow: Flow<Event>): UiState { 
 val

    event by eventFlow.collectAsState(null) return if (event = = null) { UiState.Loading } else { UiState.Data(…) } }
  45. Presenter @Composable 
 fun Presenter(eventFlow: Flow<Event>): UiState { 
 val

    event by eventFlow.collectAsState(null) return if (event = = null) { UiState.Loading } else { UiState.Data(…) } }
  46. Setup Molecule Presenter 
 (Composable)

  47. Setup Molecule Presenter 
 (Composable) StateFlow

  48. Launching Molecule val scope = CoroutineScope(Dispatchers.Main) val models: StateFlow<UsersModel> =

    scope.launchMolecule { userPresenter(postsFlow, likesFlow) }
  49. Launching Molecule val flow: StateFlow<UiState> = scope.launchMolecule { presenter(eventFlow) }

  50. Setup View Molecule

  51. Setup View View Model

  52. View Model class MyViewModel: ViewModel() { 
 
 val stateFlow

    = moleculeScope.launchMolecule { val event by eventFlow.collectAsState(null) return if (event == null) { UiState.Loading } else { UiState.Success(…) } }
  53. Learn More https: / / youtu.be/rUpZSZedoHI

  54. State Flow • Different ways to create state flows •

    Emit, collect
  55. Shared Flow

  56. Shared Flow

  57. Shared Flow Consumer 1 Consumer 2

  58. Shared Flow Consumer 1 Consumer 2

  59. Shared Flow Consumer 1 Consumer 2 Event Event

  60. Shared Flow Consumer 1 Consumer 2 Replay Replay

  61. 
 Shared Flow Buffer

  62. Shared Flow val flow = MutableSharedFlow<String>()

  63. Shared Flow val flow = MutableSharedFlow<String>() launch { flow.collect {

    } }
  64. Shared Flow val flow = MutableSharedFlow<String>() 
 launch { flow.emit("Event

    1") } launch { flow.collect { } }
  65. Shared Flow val flow = MutableSharedFlow<String>() 
 launch { flow.emit("Event

    1") } launch { flow.collect { } } Event 1
  66. Shared Flow val flow = MutableSharedFlow<String>() 
 launch { flow.emit("Event

    1”) } launch { delay(2000); flow.collect { } }
  67. Shared Flow val flow = MutableSharedFlow<String>() 
 launch { delay(2000);

    flow.collect { } } launch { flow.emit("Event 1”) }
  68. Shared Flow val flow = MutableSharedFlow<String>() 
 launch { delay(2000);

    flow.collect { } } launch { flow.emit("Event 1”) }
  69. Shared Flow val flow = MutableSharedFlow<String>() 
 launch { delay(2000);

    flow.collect { } } No value is received launch { flow.emit("Event 1”) }
  70. Shared Flow val flow = MutableSharedFlow<String>(replay = 1) 
 launch

    { flow.emit("Event 1”) } launch { delay(2000); flow.collect { } }
  71. Shared Flow val flow = MutableSharedFlow<String>(replay = 1) 
 launch

    { delay(2000); flow.collect { } } launch { flow.emit("Event 1”) } Event 1
  72. Shared Flow val flow = MutableSharedFlow<String>(replay = 1) Shared Flow

    does not complete normally launch { flow.collect { } }
  73. Cold Flows val flow = flowOf(1, 2, 3) 
 flow

    .onCompletion { } .collect { ... } Flow completes normally
  74. Shared Flow val flow = MutableSharedFlow<String>(replay = 1) val job

    = launch { flow.collect { } } job.cancel()
  75. Shared Flow val flow = MutableSharedFlow<String>(replay = 1) val job

    = launch { flow.onCompletion { }.collect { } } job.cancel() Flow completes exceptionally
  76. Shared Flow • Setup • Replay and emit • Cancellation

  77. Broadcast Channel vs Shared Flow

  78. Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

  79. val channel = BroadcastChannel<Int>(10) Broadcast Channel

  80. val channel = BroadcastChannel<Int>(10) channel.send( ... ) Broadcast Channel

  81. val channel = BroadcastChannel<Int>(10) channel.send( ... ) channel.close() Broadcast Channel

  82. val flow = MutableSharedFlow() flow.emit( ... ) Shared Flow

  83. Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

  84. val flow = MutableSharedFlow(replay = 2) flow.emit( ... ) Shared

    Flow
  85. Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

  86. val channel = BroadcastChannel<Int>(capacity = 10) channel.send( ... ) Broadcast

    Channel
  87. val flow = MutableSharedFlow( replay = 2, extraBufferCapacity = 10

    ) flow.emit( ... ) Shared Flow
  88. Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

  89. Broadcast Channel Shared Flow

  90. val channel = BroadcastChannel<Int>(capacity) val flow = MutableSharedFlow<String>(extraBufferCapacity)

  91. channel.send( ... ) channel.trySend( .. . ) flow.emit( ... )

    flow.tryEmit( ... )
  92. Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

  93. Flow Sharing Strategies

  94. Cold Flow Hot Flow Convert

  95. State Flow State Flow Flow 1 Flow 2 Flow 3

    Combined Flow
  96. Creating State & Shared Flows • shareIn • stateIn

  97. Sharing Policies • While Subscribed • Eagerly • Lazily

  98. Sharing Policies flow.shareIn( )

  99. Sharing Policies flow.shareIn( externalScope, )

  100. Sharing Policies flow.shareIn( externalScope, replay = 1, )

  101. Sharing Policies flow.shareIn( externalScope, replay = 1, started = SharingStarted.WhileSubscribed()

    )
  102. Sharing Policies val sharedFlow = flow.shareIn( externalScope, replay = 1,

    started = SharingStarted.WhileSubscribed() )
  103. Properties • Active as long as external scope is alive

    • Remains as long as there are collectors.
  104. Properties Active as long as external scope is alive externalScope.launch

    { sharedFlow.collect { } }
  105. flow.shareIn( externalScope, replay = 1, started = SharingStarted.WhileSubscribed() ) Properties

    Active as long as external scope is alive
  106. Properties Active as long as external scope is alive externalScope.cancel()

    externalScope.launch { sharedFlow.collect { } }
  107. Properties Active as long as external scope is alive externalScope.cancel()

    externalScope.launch { sharedFlow.collect { } } Complete Exceptionally
  108. Properties Active as long as there are collectors.

  109. Properties Active as long as there are collectors. val sharedFlow

    = flow.onCompletion { }.shareIn(…)
  110. Properties Active as long as there are collectors. val sharedFlow

    = flow.onCompletion { }.shareIn(…) val job = launch { sharedFlow.onCompletion { }.collect { } }
  111. Properties Active as long as there are collectors. val sharedFlow

    = flow.onCompletion { }.shareIn(…) val job = launch { sharedFlow.onCompletion { }.collect { } } job.cancel()
  112. Properties Active as long as there are collectors. val sharedFlow

    = flow.onCompletion { }.shareIn(…) val job = launch { sharedFlow.onCompletion { }.collect { } } job.cancel()
  113. Properties Active as long as there are collectors. val sharedFlow

    = flow.onCompletion { }.shareIn(…) val job = launch { sharedFlow.onCompletion { }.collect { } } job.cancel()
  114. Properties Active as long as there are collectors. val sharedFlow

    = flow.onCompletion { }.shareIn(…) val job1 = launch { sharedFlow.collect { } } val job2 = launch { sharedFlow.collect { } }
  115. Properties Active as long as there are collectors. val sharedFlow

    = flow.onCompletion { }.shareIn(…) job1.cancel() val job2 = launch { sharedFlow.collect { } }
  116. Properties Active as long as there are collectors. val sharedFlow

    = flow.onCompletion { }.shareIn(…) job1.cancel() val job2 = launch { sharedFlow.collect { } } Remain Active
  117. Properties • Active as long as external scope is alive

    • Active as long as there are collectors.
  118. Sharing Policies • While Subscribed • Eagerly • Lazily

  119. Eagerly flow.shareIn( externalScope, replay = 1, started = SharingStarted.Eagerly() )

  120. Eagerly Start producer eagerly flow .onStart { println("ON START") }

    .shareIn( ... started = SharingStarted.Eagerly)
  121. Eagerly Start producer eagerly flow .onStart { println("ON START") }

    .shareIn( ... started = SharingStarted.Eagerly)
  122. Eagerly Start producer eagerly flow .onStart { println("ON START") }

    .shareIn( ... started = SharingStarted.Eagerly) // ON START
  123. Eagerly Start producer eagerly

  124. Sharing Policies • While Subscribed • Eagerly • Lazily

  125. Lazily Start sharing after the first subscriber appears

  126. Lazily flow.shareIn( externalScope, replay = 1, started = SharingStarted.Lazily )

  127. Lazily flow .onStart { println("ON START") } .shareIn(…,started = SharingStarted.Lazily)

  128. Lazily flow .onStart { println("ON START") } .shareIn(…,started = SharingStarted.Lazily)

    launch { sharedFlow.collect { } }
  129. Lazily flow .onStart { println("ON START") } .shareIn(…,started = SharingStarted.Lazily)

    launch { sharedFlow.collect { } } // ON START
  130. Lazily Start sharing after the first subscriber appears

  131. Sharing Policies • While Subscribed • Active while there are

    active subscribers. 
 • Eagerly • Start producer eagerly and never stop 
 • Lazily • Start after the first subscriber appears and never stop
  132. Manage Backpressure

  133. 
 Shared Flow Buffer

  134. 
 Shared Flow Producer Consumer

  135. 
 Shared Flow Producer Consumer Generating events fast

  136. 
 Shared Flow Producer Consumer Listening to events 
 with

    delay
  137. 
 Shared Flow Producer Consumer

  138. 
 Shared Flow Producer Consumer

  139. 
 Shared Flow Producer Consumer What happens when it is

    full?
  140. Buffering Overflow Strategies • Suspend • Drop oldest • Drop

    latest
  141. 
 Shared Flow Producer Consumer Suspend

  142. Buffering Overflow Strategies val flow = MutableSharedFlow<String>( extraBufferCapacity = 2,

    onBufferOverflow = BufferOverflow.SUSPEND ) Buffer + Replay Count
  143. Buffering Overflow Strategies val flow = MutableSharedFlow<String>( extraBufferCapacity = 2,

    onBufferOverflow = BufferOverflow.SUSPEND )
  144. Buffering Overflow Strategies launch { flow.emit("Event 1") flow.emit("Event 2") flow.emit("Event

    3") } Suspend
  145. Buffering Overflow Strategies • Suspend • Drop oldest • Drop

    latest
  146. 
 Shared Flow Producer Consumer Drop Oldest

  147. 
 Shared Flow Producer Consumer Drop latest

  148. Buffering Overflow Strategies • Suspend • Drop oldest • Drop

    latest
  149. Kotlin State & Shared Flows in Action • State &

    Shared Flow APIs • Migrating from broadcast channels • Flow Sharing Strategies • Manage Backpressure
  150. Thank You! www.codingwithmohit.com @heyitsmohit