Slide 1

Slide 1 text

Mohit Sarveiya Migrating to Kotlin State & Shared Flows @heyitsmohit

Slide 2

Slide 2 text

Migrating to Kotlin State & Shared Flows ● State & Shared Flow APIs ● Migrating from broadcast channels ● Flow Sharing Strategies ● Manage Backpressure

Slide 3

Slide 3 text

Cold vs Hot Flows

Slide 4

Slide 4 text

What is a cold stream? ● Flow completes ● Triggers same code for every new subscriber

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

What is a hot stream? ● Never completes normally ● Exists independently of subscribers.

Slide 15

Slide 15 text

Hot Streams State Flow Shared Flow

Slide 16

Slide 16 text

State Flow ● Different ways to create state flows ● Emit, collect ● Conflation

Slide 17

Slide 17 text

State Flow View View Model

Slide 18

Slide 18 text

State Flow View View Model State

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

State Flow val stateFlow = MutableStateFlow()

Slide 23

Slide 23 text

val stateFlow = MutableStateFlow( UiState.Loading ) State Flow

Slide 24

Slide 24 text

State Flow val stateFlow = MutableStateFlow( . .. ) stateFlow.emit( 
 UIState.Success( ... ) 
 )

Slide 25

Slide 25 text

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


Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

State Flow Conflation val stateFlow = MutableStateFlow( . .. ) stateFlow.value = UIState.Success( ... ) 
 
 stateFlow.value = UIState.Error( .. . ) 
 
 stateFlow.collect { .. . } Error

Slide 30

Slide 30 text

State Flow View View Model State

Slide 31

Slide 31 text

State Flow State Flow Flow 1 Flow 2 Flow 3 Combined Flow

Slide 32

Slide 32 text

Flow Combine 1 A 1A Flow 1 Flow 2 Combined

Slide 33

Slide 33 text

Flow Combine 1 2 A 1A 2A Flow 1 Flow 2 Combined

Slide 34

Slide 34 text

Flow Combine 1 2 A 1A 2A Flow 1 Flow 2 Combined B 2B

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

Flow Marbles

Slide 39

Slide 39 text

State Flow View View Model State

Slide 40

Slide 40 text

Cash App - Molecule

Slide 41

Slide 41 text

Molecule Purpose 
 Build a StateFlow using Jetpack Compose

Slide 42

Slide 42 text

Approach View Molecule Presenter 
 (Composable) Events

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

Setup Molecule Presenter 
 (Composable)

Slide 47

Slide 47 text

Setup Molecule Presenter 
 (Composable) StateFlow

Slide 48

Slide 48 text

Launching Molecule val scope = CoroutineScope(Dispatchers.Main) val models: StateFlow = scope.launchMolecule { userPresenter(postsFlow, likesFlow) }

Slide 49

Slide 49 text

Launching Molecule val flow: StateFlow = scope.launchMolecule { presenter(eventFlow) }

Slide 50

Slide 50 text

Setup View Molecule

Slide 51

Slide 51 text

Setup View View Model

Slide 52

Slide 52 text

View Model class MyViewModel: ViewModel() { 
 
 val stateFlow = moleculeScope.launchMolecule { val event by eventFlow.collectAsState(null) return if (event == null) { UiState.Loading } else { UiState.Success(…) } }

Slide 53

Slide 53 text

Learn More https: / / youtu.be/rUpZSZedoHI

Slide 54

Slide 54 text

State Flow ● Different ways to create state flows ● Emit, collect

Slide 55

Slide 55 text

Shared Flow

Slide 56

Slide 56 text

Shared Flow

Slide 57

Slide 57 text

Shared Flow Consumer 1 Consumer 2

Slide 58

Slide 58 text

Shared Flow Consumer 1 Consumer 2

Slide 59

Slide 59 text

Shared Flow Consumer 1 Consumer 2 Event Event

Slide 60

Slide 60 text

Shared Flow Consumer 1 Consumer 2 Replay Replay

Slide 61

Slide 61 text


 Shared Flow Buffer

Slide 62

Slide 62 text

Shared Flow val flow = MutableSharedFlow()

Slide 63

Slide 63 text

Shared Flow val flow = MutableSharedFlow() launch { flow.collect { } }

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

Shared Flow val flow = MutableSharedFlow(replay = 1) Shared Flow does not complete normally launch { flow.collect { } }

Slide 73

Slide 73 text

Cold Flows val flow = flowOf(1, 2, 3) 
 flow .onCompletion { } .collect { ... } Flow completes normally

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

Shared Flow val flow = MutableSharedFlow(replay = 1) val job = launch { flow.onCompletion { }.collect { } } job.cancel() Flow completes exceptionally

Slide 76

Slide 76 text

Shared Flow ● Setup ● Replay and emit ● Cancellation

Slide 77

Slide 77 text

Broadcast Channel vs Shared Flow

Slide 78

Slide 78 text

Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

Slide 79

Slide 79 text

val channel = BroadcastChannel(10) Broadcast Channel

Slide 80

Slide 80 text

val channel = BroadcastChannel(10) channel.send( ... ) Broadcast Channel

Slide 81

Slide 81 text

val channel = BroadcastChannel(10) channel.send( ... ) channel.close() Broadcast Channel

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

Slide 86

Slide 86 text

val channel = BroadcastChannel(capacity = 10) channel.send( ... ) Broadcast Channel

Slide 87

Slide 87 text

val flow = MutableSharedFlow( replay = 2, extraBufferCapacity = 10 ) flow.emit( ... ) Shared Flow

Slide 88

Slide 88 text

Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

Slide 89

Slide 89 text

Broadcast Channel Shared Flow

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed

Slide 93

Slide 93 text

Flow Sharing Strategies

Slide 94

Slide 94 text

Cold Flow Hot Flow Convert

Slide 95

Slide 95 text

State Flow State Flow Flow 1 Flow 2 Flow 3 Combined Flow

Slide 96

Slide 96 text

Creating State & Shared Flows • shareIn • stateIn

Slide 97

Slide 97 text

Sharing Policies • While Subscribed • Eagerly • Lazily

Slide 98

Slide 98 text

Sharing Policies flow.shareIn( )

Slide 99

Slide 99 text

Sharing Policies flow.shareIn( externalScope, )

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

Properties Active as long as external scope is alive externalScope.launch { sharedFlow.collect { } }

Slide 105

Slide 105 text

flow.shareIn( externalScope, replay = 1, started = SharingStarted.WhileSubscribed() ) Properties Active as long as external scope is alive

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

Properties Active as long as there are collectors.

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

Properties • Active as long as external scope is alive • Active as long as there are collectors.

Slide 118

Slide 118 text

Sharing Policies • While Subscribed • Eagerly • Lazily

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

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

Slide 123

Slide 123 text

Eagerly Start producer eagerly

Slide 124

Slide 124 text

Sharing Policies • While Subscribed • Eagerly • Lazily

Slide 125

Slide 125 text

Lazily Start sharing after the first subscriber appears

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

Lazily Start sharing after the first subscriber appears

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

Manage Backpressure

Slide 133

Slide 133 text


 Shared Flow Buffer

Slide 134

Slide 134 text


 Shared Flow Producer Consumer

Slide 135

Slide 135 text


 Shared Flow Producer Consumer Generating events fast

Slide 136

Slide 136 text


 Shared Flow Producer Consumer Listening to events 
 with delay

Slide 137

Slide 137 text


 Shared Flow Producer Consumer

Slide 138

Slide 138 text


 Shared Flow Producer Consumer

Slide 139

Slide 139 text


 Shared Flow Producer Consumer What happens when it is full?

Slide 140

Slide 140 text

Buffering Overflow Strategies • Suspend • Drop oldest • Drop latest

Slide 141

Slide 141 text


 Shared Flow Producer Consumer Suspend

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

Buffering Overflow Strategies val flow = MutableSharedFlow( extraBufferCapacity = 2, onBufferOverflow = BufferOverflow.SUSPEND )

Slide 144

Slide 144 text

Buffering Overflow Strategies launch { flow.emit("Event 1") flow.emit("Event 2") flow.emit("Event 3") } Suspend

Slide 145

Slide 145 text

Buffering Overflow Strategies • Suspend • Drop oldest • Drop latest

Slide 146

Slide 146 text


 Shared Flow Producer Consumer Drop Oldest

Slide 147

Slide 147 text


 Shared Flow Producer Consumer Drop latest

Slide 148

Slide 148 text

Buffering Overflow Strategies • Suspend • Drop oldest • Drop latest

Slide 149

Slide 149 text

Kotlin State & Shared Flows in Action ● State & Shared Flow APIs ● Migrating from broadcast channels ● Flow Sharing Strategies ● Manage Backpressure

Slide 150

Slide 150 text

Thank You! www.codingwithmohit.com @heyitsmohit