Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Migrating to Kotlin State & Shared Flows
Search
Mohit S
June 03, 2022
Programming
1
800
Migrating to Kotlin State & Shared Flows
Mohit S
June 03, 2022
Tweet
Share
More Decks by Mohit S
See All by Mohit S
Guide to Improving Compose Performance
heyitsmohit
0
250
Building Shared UIs across Platforms with Compose
heyitsmohit
1
640
Building Multiplatform Apps with Compose
heyitsmohit
2
520
Building StateFlows with Jetpack Compose
heyitsmohit
6
1.9k
Building Android Testing Infrastructure
heyitsmohit
1
500
Using Square Workflow for Android & iOS
heyitsmohit
1
430
Building Android Infrastructure Teams at Scale
heyitsmohit
3
330
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
580
Challenges of Building Kotlin Multiplatform Libraries
heyitsmohit
1
450
Other Decks in Programming
See All in Programming
あなたの知らない「動画広告」の世界 - iOSDC Japan 2025
ukitaka
0
370
ソフトウェア設計の実践的な考え方
masuda220
PRO
3
460
ポスターセッション: 「まっすぐ行って、右!」って言ってラズパイカーを動かしたい 〜生成AI × Raspberry Pi Pico × Gradioの試作メモ〜
komofr
0
940
Advance Your Career with Open Source
ivargrimstad
0
320
CSC305 Lecture 03
javiergs
PRO
0
230
デミカツ切り抜きで面倒くさいことはPythonにやらせよう
aokswork3
0
180
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
110
iOSエンジニア向けの英語学習アプリを作る!
yukawashouhei
0
170
プログラマのための作曲入門
cheebow
0
540
アメ車でサンノゼを走ってきたよ!
s_shimotori
0
140
Web フロントエンドエンジニアに開かれる AI Agent プロダクト開発 - Vercel AI SDK を観察して AI Agent と仲良くなろう! #FEC余熱NIGHT
izumin5210
3
380
Django Ninja による API 開発効率化とリプレースの実践
kashewnuts
0
920
Featured
See All Featured
Visualization
eitanlees
148
16k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
54
3k
Optimizing for Happiness
mojombo
379
70k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.2k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
How to Ace a Technical Interview
jacobian
280
23k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
29
2.6k
Code Reviewing Like a Champion
maltzj
525
40k
Agile that works and the tools we love
rasmusluckow
331
21k
Statistics for Hackers
jakevdp
799
220k
Building a Modern Day E-commerce SEO Strategy
aleyda
43
7.7k
Transcript
Mohit Sarveiya Migrating to Kotlin State & Shared Flows @heyitsmohit
Migrating to Kotlin State & Shared Flows • State &
Shared Flow APIs • Migrating from broadcast channels • Flow Sharing Strategies • Manage Backpressure
Cold vs Hot Flows
What is a cold stream? • Flow completes • Triggers
same code for every new subscriber
Cold Flows val flow = flowOf(1, 2, 3) .map
{ it + 1 }
Cold Flows val flow = flowOf(1, 2, 3) .map
{ it + 1 } flow.collect { ... }
Cold Flows val flow = flowOf(1, 2, 3) .map
{ it + 1 } flow.collect { ... }
Cold Flows val flow = flowOf(1, 2, 3) .map
{ it + 1 } flow.collect { ... }
Cold Flows val flow = flowOf(1, 2, 3) .map
{ it + 1 } flow.collect { // 2, 3, 4 }
Cold Flows val flow = flowOf(1, 2, 3) .map
{ it + 1 } flow.collect { ... } flow.collect { ... }
Cold Flows val flow = flowOf(1, 2, 3) .map
{ it + 1 } flow.collect { ... } flow.collect { ... }
Cold Flows val flow = flowOf(1, 2, 3) .map
{ it + 1 } flow.collect { ... } flow.collect { ... }
Cold Flows val flow = flowOf(1, 2, 3) .map
{ it + 1 } flow.collect { ... } flow.collect { // 2, 3, 4 }
What is a hot stream? • Never completes normally •
Exists independently of subscribers.
Hot Streams State Flow Shared Flow
State Flow • Different ways to create state flows •
Emit, collect • Conflation
State Flow View View Model
State Flow View View Model State
State Flow sealed class UiState { object Loading: UiState()
data class Success(…): UiState() data class Error(…): UiState() }
State Flow sealed class UiState { object Loading: UiState()
data class Success(…): UiState() data class Error(…): UiState() }
State Flow sealed class UiState { object Loading: UiState()
data class Success(…): UiState() data class Error(…): UiState() }
State Flow val stateFlow = MutableStateFlow()
val stateFlow = MutableStateFlow( UiState.Loading ) State Flow
State Flow val stateFlow = MutableStateFlow( . .. ) stateFlow.emit(
UIState.Success( ... ) )
State Flow val stateFlow = MutableStateFlow( . .. ) stateFlow.value
= UIState.Success( ... )
State Flow val stateFlow = MutableStateFlow( . .. ) stateFlow.update
{ UIState.Success( .. . ) }
State Flow val stateFlow = MutableStateFlow( . .. ) stateFlow.collect
{ .. . } stateFlow.collect { .. . } Latest value is received
State Flow Conflation val stateFlow = MutableStateFlow( . .. )
stateFlow.value = UIState.Success( ... ) stateFlow.value = UIState.Error( .. . ) Conflate
State Flow Conflation val stateFlow = MutableStateFlow( . .. )
stateFlow.value = UIState.Success( ... ) stateFlow.value = UIState.Error( .. . ) stateFlow.collect { .. . } Error
State Flow View View Model State
State Flow State Flow Flow 1 Flow 2 Flow 3
Combined Flow
Flow Combine 1 A 1A Flow 1 Flow 2 Combined
Flow Combine 1 2 A 1A 2A Flow 1 Flow
2 Combined
Flow Combine 1 2 A 1A 2A Flow 1 Flow
2 Combined B 2B
val stateFlow = MutableStateFlow(UIState.Loading) flow1.combine(flow2) { a, b -
> } State Flow
val stateFlow = MutableStateFlow(UIState.Loading) flow1.combine(flow2) { a, b -
> combineItems(a, b) } State Flow
val stateFlow = MutableStateFlow(UIState.Loading) flow1.combine(flow2) { a, b -
> combineItems(a, b) }.collect { stateFlow.emit(it) } State Flow
Flow Marbles
State Flow View View Model State
Cash App - Molecule
Molecule Purpose Build a StateFlow using Jetpack Compose
Approach View Molecule Presenter (Composable) Events
Presenter @Composable fun Presenter(eventFlow: Flow<Event>): UiState { val
event by eventFlow.collectAsState(null) return if (event = = null) { UiState.Loading } else { UiState.Data(…) } }
Presenter @Composable fun Presenter(eventFlow: Flow<Event>): UiState { val
event by eventFlow.collectAsState(null) return if (event = = null) { UiState.Loading } else { UiState.Data(…) } }
Presenter @Composable fun Presenter(eventFlow: Flow<Event>): UiState { val
event by eventFlow.collectAsState(null) return if (event = = null) { UiState.Loading } else { UiState.Data(…) } }
Setup Molecule Presenter (Composable)
Setup Molecule Presenter (Composable) StateFlow
Launching Molecule val scope = CoroutineScope(Dispatchers.Main) val models: StateFlow<UsersModel> =
scope.launchMolecule { userPresenter(postsFlow, likesFlow) }
Launching Molecule val flow: StateFlow<UiState> = scope.launchMolecule { presenter(eventFlow) }
Setup View Molecule
Setup View View Model
View Model class MyViewModel: ViewModel() { val stateFlow
= moleculeScope.launchMolecule { val event by eventFlow.collectAsState(null) return if (event == null) { UiState.Loading } else { UiState.Success(…) } }
Learn More https: / / youtu.be/rUpZSZedoHI
State Flow • Different ways to create state flows •
Emit, collect
Shared Flow
Shared Flow
Shared Flow Consumer 1 Consumer 2
Shared Flow Consumer 1 Consumer 2
Shared Flow Consumer 1 Consumer 2 Event Event
Shared Flow Consumer 1 Consumer 2 Replay Replay
Shared Flow Buffer
Shared Flow val flow = MutableSharedFlow<String>()
Shared Flow val flow = MutableSharedFlow<String>() launch { flow.collect {
} }
Shared Flow val flow = MutableSharedFlow<String>() launch { flow.emit("Event
1") } launch { flow.collect { } }
Shared Flow val flow = MutableSharedFlow<String>() launch { flow.emit("Event
1") } launch { flow.collect { } } Event 1
Shared Flow val flow = MutableSharedFlow<String>() launch { flow.emit("Event
1”) } launch { delay(2000); flow.collect { } }
Shared Flow val flow = MutableSharedFlow<String>() launch { delay(2000);
flow.collect { } } launch { flow.emit("Event 1”) }
Shared Flow val flow = MutableSharedFlow<String>() launch { delay(2000);
flow.collect { } } launch { flow.emit("Event 1”) }
Shared Flow val flow = MutableSharedFlow<String>() launch { delay(2000);
flow.collect { } } No value is received launch { flow.emit("Event 1”) }
Shared Flow val flow = MutableSharedFlow<String>(replay = 1) launch
{ flow.emit("Event 1”) } launch { delay(2000); flow.collect { } }
Shared Flow val flow = MutableSharedFlow<String>(replay = 1) launch
{ delay(2000); flow.collect { } } launch { flow.emit("Event 1”) } Event 1
Shared Flow val flow = MutableSharedFlow<String>(replay = 1) Shared Flow
does not complete normally launch { flow.collect { } }
Cold Flows val flow = flowOf(1, 2, 3) flow
.onCompletion { } .collect { ... } Flow completes normally
Shared Flow val flow = MutableSharedFlow<String>(replay = 1) val job
= launch { flow.collect { } } job.cancel()
Shared Flow val flow = MutableSharedFlow<String>(replay = 1) val job
= launch { flow.onCompletion { }.collect { } } job.cancel() Flow completes exceptionally
Shared Flow • Setup • Replay and emit • Cancellation
Broadcast Channel vs Shared Flow
Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed
val channel = BroadcastChannel<Int>(10) Broadcast Channel
val channel = BroadcastChannel<Int>(10) channel.send( ... ) Broadcast Channel
val channel = BroadcastChannel<Int>(10) channel.send( ... ) channel.close() Broadcast Channel
val flow = MutableSharedFlow() flow.emit( ... ) Shared Flow
Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed
val flow = MutableSharedFlow(replay = 2) flow.emit( ... ) Shared
Flow
Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed
val channel = BroadcastChannel<Int>(capacity = 10) channel.send( ... ) Broadcast
Channel
val flow = MutableSharedFlow( replay = 2, extraBufferCapacity = 10
) flow.emit( ... ) Shared Flow
Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed
Broadcast Channel Shared Flow
val channel = BroadcastChannel<Int>(capacity) val flow = MutableSharedFlow<String>(extraBufferCapacity)
channel.send( ... ) channel.trySend( .. . ) flow.emit( ... )
flow.tryEmit( ... )
Broadcast Channel Shared Flow Channel APIs Replay Buffer Closed
Flow Sharing Strategies
Cold Flow Hot Flow Convert
State Flow State Flow Flow 1 Flow 2 Flow 3
Combined Flow
Creating State & Shared Flows • shareIn • stateIn
Sharing Policies • While Subscribed • Eagerly • Lazily
Sharing Policies flow.shareIn( )
Sharing Policies flow.shareIn( externalScope, )
Sharing Policies flow.shareIn( externalScope, replay = 1, )
Sharing Policies flow.shareIn( externalScope, replay = 1, started = SharingStarted.WhileSubscribed()
)
Sharing Policies val sharedFlow = flow.shareIn( externalScope, replay = 1,
started = SharingStarted.WhileSubscribed() )
Properties • Active as long as external scope is alive
• Remains as long as there are collectors.
Properties Active as long as external scope is alive externalScope.launch
{ sharedFlow.collect { } }
flow.shareIn( externalScope, replay = 1, started = SharingStarted.WhileSubscribed() ) Properties
Active as long as external scope is alive
Properties Active as long as external scope is alive externalScope.cancel()
externalScope.launch { sharedFlow.collect { } }
Properties Active as long as external scope is alive externalScope.cancel()
externalScope.launch { sharedFlow.collect { } } Complete Exceptionally
Properties Active as long as there are collectors.
Properties Active as long as there are collectors. val sharedFlow
= flow.onCompletion { }.shareIn(…)
Properties Active as long as there are collectors. val sharedFlow
= flow.onCompletion { }.shareIn(…) val job = launch { sharedFlow.onCompletion { }.collect { } }
Properties Active as long as there are collectors. val sharedFlow
= flow.onCompletion { }.shareIn(…) val job = launch { sharedFlow.onCompletion { }.collect { } } job.cancel()
Properties Active as long as there are collectors. val sharedFlow
= flow.onCompletion { }.shareIn(…) val job = launch { sharedFlow.onCompletion { }.collect { } } job.cancel()
Properties Active as long as there are collectors. val sharedFlow
= flow.onCompletion { }.shareIn(…) val job = launch { sharedFlow.onCompletion { }.collect { } } job.cancel()
Properties Active as long as there are collectors. val sharedFlow
= flow.onCompletion { }.shareIn(…) val job1 = launch { sharedFlow.collect { } } val job2 = launch { sharedFlow.collect { } }
Properties Active as long as there are collectors. val sharedFlow
= flow.onCompletion { }.shareIn(…) job1.cancel() val job2 = launch { sharedFlow.collect { } }
Properties Active as long as there are collectors. val sharedFlow
= flow.onCompletion { }.shareIn(…) job1.cancel() val job2 = launch { sharedFlow.collect { } } Remain Active
Properties • Active as long as external scope is alive
• Active as long as there are collectors.
Sharing Policies • While Subscribed • Eagerly • Lazily
Eagerly flow.shareIn( externalScope, replay = 1, started = SharingStarted.Eagerly() )
Eagerly Start producer eagerly flow .onStart { println("ON START") }
.shareIn( ... started = SharingStarted.Eagerly)
Eagerly Start producer eagerly flow .onStart { println("ON START") }
.shareIn( ... started = SharingStarted.Eagerly)
Eagerly Start producer eagerly flow .onStart { println("ON START") }
.shareIn( ... started = SharingStarted.Eagerly) // ON START
Eagerly Start producer eagerly
Sharing Policies • While Subscribed • Eagerly • Lazily
Lazily Start sharing after the first subscriber appears
Lazily flow.shareIn( externalScope, replay = 1, started = SharingStarted.Lazily )
Lazily flow .onStart { println("ON START") } .shareIn(…,started = SharingStarted.Lazily)
Lazily flow .onStart { println("ON START") } .shareIn(…,started = SharingStarted.Lazily)
launch { sharedFlow.collect { } }
Lazily flow .onStart { println("ON START") } .shareIn(…,started = SharingStarted.Lazily)
launch { sharedFlow.collect { } } // ON START
Lazily Start sharing after the first subscriber appears
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
Manage Backpressure
Shared Flow Buffer
Shared Flow Producer Consumer
Shared Flow Producer Consumer Generating events fast
Shared Flow Producer Consumer Listening to events with
delay
Shared Flow Producer Consumer
Shared Flow Producer Consumer
Shared Flow Producer Consumer What happens when it is
full?
Buffering Overflow Strategies • Suspend • Drop oldest • Drop
latest
Shared Flow Producer Consumer Suspend
Buffering Overflow Strategies val flow = MutableSharedFlow<String>( extraBufferCapacity = 2,
onBufferOverflow = BufferOverflow.SUSPEND ) Buffer + Replay Count
Buffering Overflow Strategies val flow = MutableSharedFlow<String>( extraBufferCapacity = 2,
onBufferOverflow = BufferOverflow.SUSPEND )
Buffering Overflow Strategies launch { flow.emit("Event 1") flow.emit("Event 2") flow.emit("Event
3") } Suspend
Buffering Overflow Strategies • Suspend • Drop oldest • Drop
latest
Shared Flow Producer Consumer Drop Oldest
Shared Flow Producer Consumer Drop latest
Buffering Overflow Strategies • Suspend • Drop oldest • Drop
latest
Kotlin State & Shared Flows in Action • State &
Shared Flow APIs • Migrating from broadcast channels • Flow Sharing Strategies • Manage Backpressure
Thank You! www.codingwithmohit.com @heyitsmohit