$30 off During Our Annual Pro Sale. View Details »

Migrating to Kotlin State & Shared Flows

Migrating to Kotlin State & Shared Flows

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

    View Slide

  2. Migrating to Kotlin State & Shared Flows
    ● State & Shared Flow APIs

    ● Migrating from broadcast channels

    ● Flow Sharing Strategies

    ● Manage Backpressure

    View Slide

  3. Cold vs Hot Flows

    View Slide

  4. What is a cold stream?
    ● Flow completes

    ● Triggers same code for every new subscriber

    View Slide

  5. Cold Flows
    val flow = flowOf(1, 2, 3)

    .map { it + 1 }

    View Slide

  6. Cold Flows
    val flow = flowOf(1, 2, 3)

    .map { it + 1 }
    flow.collect {

    ...


    }

    View Slide

  7. Cold Flows
    val flow = flowOf(1, 2, 3)

    .map { it + 1 }
    flow.collect {

    ...


    }

    View Slide

  8. Cold Flows
    val flow = flowOf(1, 2, 3)

    .map { it + 1 }
    flow.collect {

    ...


    }

    View Slide

  9. Cold Flows
    val flow = flowOf(1, 2, 3)

    .map { it + 1 }
    flow.collect {

    //
    2, 3, 4

    }

    View Slide

  10. Cold Flows
    val flow = flowOf(1, 2, 3)

    .map { it + 1 }
    flow.collect {

    ...


    }

    flow.collect {

    ...


    }

    View Slide

  11. Cold Flows
    val flow = flowOf(1, 2, 3)

    .map { it + 1 }
    flow.collect {

    ...


    }

    flow.collect {

    ...


    }

    View Slide

  12. Cold Flows
    val flow = flowOf(1, 2, 3)

    .map { it + 1 }
    flow.collect {

    ...


    }

    flow.collect {

    ...


    }

    View Slide

  13. Cold Flows
    val flow = flowOf(1, 2, 3)

    .map { it + 1 }
    flow.collect {

    ...


    }

    flow.collect {

    //
    2, 3, 4

    }

    View Slide

  14. What is a hot stream?
    ● Never completes normally

    ● Exists independently of subscribers.

    View Slide

  15. Hot Streams
    State Flow Shared Flow

    View Slide

  16. State Flow
    ● Different ways to create state flows

    ● Emit, collect

    ● Conflation

    View Slide

  17. State Flow
    View View Model

    View Slide

  18. State Flow
    View View Model
    State

    View Slide

  19. State Flow
    sealed class UiState {

    object Loading: UiState()

    data class Success(…): UiState()

    data class Error(…): UiState()

    }

    View Slide

  20. State Flow
    sealed class UiState {

    object Loading: UiState()

    data class Success(…): UiState()

    data class Error(…): UiState()

    }

    View Slide

  21. State Flow
    sealed class UiState {

    object Loading: UiState()

    data class Success(…): UiState()

    data class Error(…): UiState()

    }

    View Slide

  22. State Flow
    val stateFlow = MutableStateFlow()

    View Slide

  23. val stateFlow = MutableStateFlow(

    UiState.Loading

    )

    State Flow

    View Slide

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

    stateFlow.emit(

    UIState.Success(
    ...
    )

    )

    View Slide

  25. State Flow
    val stateFlow = MutableStateFlow(
    . ..
    )

    stateFlow.value = UIState.Success(
    ...
    )

    View Slide

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

    stateFlow.update {

    UIState.Success(
    .. .
    )

    }

    View Slide

  27. State Flow
    val stateFlow = MutableStateFlow(
    . ..
    )

    stateFlow.collect {
    .. .
    }


    stateFlow.collect {
    .. .
    }
    Latest value is received

    View Slide

  28. State Flow Conflation
    val stateFlow = MutableStateFlow(
    . ..
    )

    stateFlow.value = UIState.Success(
    ...
    )


    stateFlow.value = UIState.Error(
    .. .
    ) Conflate

    View Slide

  29. State Flow Conflation
    val stateFlow = MutableStateFlow(
    . ..
    )

    stateFlow.value = UIState.Success(
    ...
    )


    stateFlow.value = UIState.Error(
    .. .
    )


    stateFlow.collect {
    .. .
    } Error

    View Slide

  30. State Flow
    View View Model
    State

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  35. val stateFlow = MutableStateFlow(UIState.Loading)

    flow1.combine(flow2) { a, b
    - >


    }

    State Flow

    View Slide

  36. val stateFlow = MutableStateFlow(UIState.Loading)

    flow1.combine(flow2) { a, b
    - >


    combineItems(a, b)

    }

    State Flow

    View Slide

  37. val stateFlow = MutableStateFlow(UIState.Loading)

    flow1.combine(flow2) { a, b
    - >


    combineItems(a, b)

    }.collect {

    stateFlow.emit(it)

    }

    State Flow

    View Slide

  38. Flow Marbles

    View Slide

  39. State Flow
    View View Model
    State

    View Slide

  40. Cash App - Molecule

    View Slide

  41. Molecule
    Purpose

    Build a StateFlow using Jetpack Compose

    View Slide

  42. Approach
    View Molecule
    Presenter

    (Composable)
    Events

    View Slide

  43. Presenter
    @Composable

    fun Presenter(eventFlow: Flow): UiState {

    val event by eventFlow.collectAsState(null)

    return if (event
    = =
    null) {

    UiState.Loading

    } else {

    UiState.Data(…)

    }

    }

    View Slide

  44. Presenter
    @Composable

    fun Presenter(eventFlow: Flow): UiState {

    val event by eventFlow.collectAsState(null)

    return if (event
    = =
    null) {

    UiState.Loading

    } else {

    UiState.Data(…)

    }

    }

    View Slide

  45. Presenter
    @Composable

    fun Presenter(eventFlow: Flow): UiState {

    val event by eventFlow.collectAsState(null)

    return if (event
    = =
    null) {

    UiState.Loading

    } else {

    UiState.Data(…)

    }

    }

    View Slide

  46. Setup
    Molecule
    Presenter

    (Composable)

    View Slide

  47. Setup
    Molecule
    Presenter

    (Composable)
    StateFlow

    View Slide

  48. Launching Molecule
    val scope = CoroutineScope(Dispatchers.Main)

    val models: StateFlow = scope.launchMolecule {

    userPresenter(postsFlow, likesFlow)

    }

    View Slide

  49. Launching Molecule
    val flow: StateFlow = scope.launchMolecule {

    presenter(eventFlow)

    }

    View Slide

  50. Setup
    View Molecule

    View Slide

  51. Setup
    View View Model

    View Slide

  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(…)

    }

    }

    View Slide

  53. Learn More
    https:
    /
    /
    youtu.be/rUpZSZedoHI

    View Slide

  54. State Flow
    ● Different ways to create state flows

    ● Emit, collect

    View Slide

  55. Shared Flow

    View Slide

  56. Shared Flow

    View Slide

  57. Shared Flow
    Consumer 1 Consumer 2

    View Slide

  58. Shared Flow
    Consumer 1 Consumer 2

    View Slide

  59. Shared Flow
    Consumer 1 Consumer 2
    Event Event

    View Slide

  60. Shared Flow
    Consumer 1 Consumer 2
    Replay Replay

    View Slide


  61. Shared Flow
    Buffer

    View Slide

  62. Shared Flow
    val flow = MutableSharedFlow()

    View Slide

  63. Shared Flow
    val flow = MutableSharedFlow()

    launch { flow.collect { } }

    View Slide

  64. Shared Flow
    val flow = MutableSharedFlow()


    launch { flow.emit("Event 1") }

    launch { flow.collect { } }

    View Slide

  65. Shared Flow
    val flow = MutableSharedFlow()


    launch { flow.emit("Event 1") }

    launch { flow.collect { } }
    Event 1

    View Slide

  66. Shared Flow
    val flow = MutableSharedFlow()


    launch { flow.emit("Event 1”) }

    launch { delay(2000); flow.collect { } }

    View Slide

  67. Shared Flow
    val flow = MutableSharedFlow()


    launch { delay(2000); flow.collect { } }

    launch { flow.emit("Event 1”) }

    View Slide

  68. Shared Flow
    val flow = MutableSharedFlow()


    launch { delay(2000); flow.collect { } }

    launch { flow.emit("Event 1”) }

    View Slide

  69. Shared Flow
    val flow = MutableSharedFlow()


    launch { delay(2000); flow.collect { } }
    No value is received
    launch { flow.emit("Event 1”) }

    View Slide

  70. Shared Flow
    val flow = MutableSharedFlow(replay = 1)


    launch { flow.emit("Event 1”) }

    launch { delay(2000); flow.collect { } }

    View Slide

  71. Shared Flow
    val flow = MutableSharedFlow(replay = 1)


    launch { delay(2000); flow.collect { } }

    launch { flow.emit("Event 1”) }

    Event 1

    View Slide

  72. Shared Flow
    val flow = MutableSharedFlow(replay = 1)

    Shared Flow does not complete normally
    launch { flow.collect { } }

    View Slide

  73. Cold Flows
    val flow = flowOf(1, 2, 3)

    flow

    .onCompletion { }

    .collect {

    ...


    }

    Flow completes normally

    View Slide

  74. Shared Flow
    val flow = MutableSharedFlow(replay = 1)

    val job = launch { flow.collect { } }

    job.cancel()

    View Slide

  75. Shared Flow
    val flow = MutableSharedFlow(replay = 1)

    val job = launch { flow.onCompletion { }.collect { } }

    job.cancel()

    Flow completes exceptionally

    View Slide

  76. Shared Flow
    ● Setup

    ● Replay and emit

    ● Cancellation

    View Slide

  77. Broadcast Channel vs Shared Flow

    View Slide

  78. Broadcast
    Channel
    Shared Flow
    Channel APIs
    Replay
    Buffer
    Closed

    View Slide

  79. val channel = BroadcastChannel(10)

    Broadcast Channel

    View Slide

  80. val channel = BroadcastChannel(10)

    channel.send(
    ...
    )
    Broadcast Channel

    View Slide

  81. val channel = BroadcastChannel(10)

    channel.send(
    ...
    )

    channel.close()
    Broadcast Channel

    View Slide

  82. val flow = MutableSharedFlow()

    flow.emit(
    ...
    )

    Shared Flow

    View Slide

  83. Broadcast
    Channel
    Shared Flow
    Channel APIs
    Replay
    Buffer
    Closed

    View Slide

  84. val flow = MutableSharedFlow(replay = 2)

    flow.emit(
    ...
    )

    Shared Flow

    View Slide

  85. Broadcast
    Channel
    Shared Flow
    Channel APIs
    Replay
    Buffer
    Closed

    View Slide

  86. val channel = BroadcastChannel(capacity = 10)

    channel.send(
    ...
    )
    Broadcast Channel

    View Slide

  87. val flow = MutableSharedFlow(

    replay = 2,

    extraBufferCapacity = 10

    )

    flow.emit(
    ...
    )

    Shared Flow

    View Slide

  88. Broadcast
    Channel
    Shared Flow
    Channel APIs
    Replay
    Buffer
    Closed

    View Slide

  89. Broadcast Channel Shared Flow

    View Slide

  90. val channel = BroadcastChannel(capacity)
    val flow = MutableSharedFlow(extraBufferCapacity)

    View Slide

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

    View Slide

  92. Broadcast
    Channel
    Shared Flow
    Channel APIs
    Replay
    Buffer
    Closed

    View Slide

  93. Flow Sharing Strategies

    View Slide

  94. Cold Flow Hot Flow
    Convert

    View Slide

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

    View Slide

  96. Creating State & Shared Flows
    • shareIn

    • stateIn

    View Slide

  97. Sharing Policies
    • While Subscribed

    • Eagerly

    • Lazily

    View Slide

  98. Sharing Policies
    flow.shareIn(

    )

    View Slide

  99. Sharing Policies
    flow.shareIn(

    externalScope,

    )

    View Slide

  100. Sharing Policies
    flow.shareIn(

    externalScope,

    replay = 1,

    )

    View Slide

  101. Sharing Policies
    flow.shareIn(

    externalScope,

    replay = 1,

    started = SharingStarted.WhileSubscribed()

    )

    View Slide

  102. Sharing Policies
    val sharedFlow = flow.shareIn(

    externalScope,

    replay = 1,

    started = SharingStarted.WhileSubscribed()

    )

    View Slide

  103. Properties
    • Active as long as external scope is alive

    • Remains as long as there are collectors.

    View Slide

  104. Properties
    Active as long as external scope is alive
    externalScope.launch {

    sharedFlow.collect { }

    }

    View Slide

  105. flow.shareIn(

    externalScope,

    replay = 1,

    started = SharingStarted.WhileSubscribed()

    )

    Properties
    Active as long as external scope is alive

    View Slide

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

    sharedFlow.collect { }

    }

    View Slide

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

    sharedFlow.collect { }

    }
    Complete Exceptionally

    View Slide

  108. Properties
    Active as long as there are collectors.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  117. Properties
    • Active as long as external scope is alive

    • Active as long as there are collectors.

    View Slide

  118. Sharing Policies
    • While Subscribed

    • Eagerly

    • Lazily

    View Slide

  119. Eagerly
    flow.shareIn(

    externalScope,

    replay = 1,

    started = SharingStarted.Eagerly()

    )

    View Slide

  120. Eagerly
    Start producer eagerly

    flow

    .onStart { println("ON START") }

    .shareIn(
    ...
    started = SharingStarted.Eagerly)

    View Slide

  121. Eagerly
    Start producer eagerly

    flow

    .onStart { println("ON START") }

    .shareIn(
    ...
    started = SharingStarted.Eagerly)

    View Slide

  122. Eagerly
    Start producer eagerly

    flow

    .onStart { println("ON START") }

    .shareIn(
    ...
    started = SharingStarted.Eagerly)

    //
    ON START

    View Slide

  123. Eagerly
    Start producer eagerly

    View Slide

  124. Sharing Policies
    • While Subscribed

    • Eagerly

    • Lazily

    View Slide

  125. Lazily
    Start sharing after the first subscriber appears

    View Slide

  126. Lazily
    flow.shareIn(

    externalScope,

    replay = 1,

    started = SharingStarted.Lazily

    )

    View Slide

  127. Lazily
    flow

    .onStart { println("ON START") }

    .shareIn(…,started = SharingStarted.Lazily)

    View Slide

  128. Lazily
    flow

    .onStart { println("ON START") }

    .shareIn(…,started = SharingStarted.Lazily)

    launch { sharedFlow.collect { } }

    View Slide

  129. Lazily
    flow

    .onStart { println("ON START") }

    .shareIn(…,started = SharingStarted.Lazily)

    launch { sharedFlow.collect { } }
    //
    ON START

    View Slide

  130. Lazily
    Start sharing after the first subscriber appears

    View Slide

  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

    View Slide

  132. Manage Backpressure

    View Slide


  133. Shared Flow
    Buffer

    View Slide


  134. Shared Flow
    Producer Consumer

    View Slide


  135. Shared Flow
    Producer Consumer
    Generating events fast

    View Slide


  136. Shared Flow
    Producer Consumer
    Listening to events

    with delay

    View Slide


  137. Shared Flow
    Producer Consumer

    View Slide


  138. Shared Flow
    Producer Consumer

    View Slide


  139. Shared Flow
    Producer Consumer
    What happens when it is full?

    View Slide

  140. Buffering Overflow Strategies
    • Suspend

    • Drop oldest

    • Drop latest

    View Slide


  141. Shared Flow
    Producer Consumer
    Suspend

    View Slide

  142. Buffering Overflow Strategies
    val flow = MutableSharedFlow(

    extraBufferCapacity = 2,

    onBufferOverflow = BufferOverflow.SUSPEND

    )
    Buffer + Replay Count

    View Slide

  143. Buffering Overflow Strategies
    val flow = MutableSharedFlow(

    extraBufferCapacity = 2,

    onBufferOverflow = BufferOverflow.SUSPEND

    )

    View Slide

  144. Buffering Overflow Strategies
    launch {

    flow.emit("Event 1")

    flow.emit("Event 2")

    flow.emit("Event 3")

    }

    Suspend

    View Slide

  145. Buffering Overflow Strategies
    • Suspend

    • Drop oldest

    • Drop latest

    View Slide


  146. Shared Flow
    Producer Consumer
    Drop Oldest

    View Slide


  147. Shared Flow
    Producer Consumer
    Drop latest

    View Slide

  148. Buffering Overflow Strategies
    • Suspend

    • Drop oldest

    • Drop latest

    View Slide

  149. Kotlin State & Shared Flows in Action
    ● State & Shared Flow APIs

    ● Migrating from broadcast channels

    ● Flow Sharing Strategies

    ● Manage Backpressure

    View Slide

  150. Thank You!
    www.codingwithmohit.com
    @heyitsmohit

    View Slide