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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Mohit S
June 03, 2022
Programming
850
1
Share
Migrating to Kotlin State & Shared Flows
Mohit S
June 03, 2022
More Decks by Mohit S
See All by Mohit S
Guide to Improving Compose Performance
heyitsmohit
0
320
Building Shared UIs across Platforms with Compose
heyitsmohit
1
700
Building Multiplatform Apps with Compose
heyitsmohit
2
600
Building StateFlows with Jetpack Compose
heyitsmohit
6
2k
Building Android Testing Infrastructure
heyitsmohit
1
600
Using Square Workflow for Android & iOS
heyitsmohit
1
500
Building Android Infrastructure Teams at Scale
heyitsmohit
3
400
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
640
Challenges of Building Kotlin Multiplatform Libraries
heyitsmohit
1
510
Other Decks in Programming
See All in Programming
AIベース静的検査器の偽陽性率を抑える工夫3選
orgachem
PRO
3
350
セグメントとターゲットを意識するプロポーザルの書き方 〜採択の鍵は、誰に刺すかを見極めるマーケティング戦略にある〜
m3m0r7
PRO
0
580
感情を設計する
ichimichi
5
1.5k
Kingdom of the Machine
yui_knk
2
810
検索設計から 推論設計への重心移動と Recall-First Retrieval
po3rin
3
980
Oxlintとeslint-plugin-react-hooks 明日から始められそう?
t6adev
0
280
Swift Concurrency Type System
inamiy
1
540
Road to RubyKaigi: Play Hard(ware)
makicamel
1
410
実用!Hono RPC2026
yodaka
2
250
TiDBのアーキテクチャから学ぶ分散システム入門 〜MySQL互換のNewSQLは何を解決するのか〜 / tidb-architecture-study
dznbk
1
180
Cache-moi si tu peux : patterns et pièges du cache en production - Devoxx France 2026 - Conférence
slecache
0
280
クラウドネイティブなエンジニアに向ける Raycastの魅力と実際の活用事例
nealle
2
220
Featured
See All Featured
Bash Introduction
62gerente
615
210k
Why Our Code Smells
bkeepers
PRO
340
58k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.4k
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Deep Space Network (abreviated)
tonyrice
0
120
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
120
Speed Design
sergeychernyshev
33
1.6k
Optimising Largest Contentful Paint
csswizardry
37
3.6k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
480
Designing Powerful Visuals for Engaging Learning
tmiket
1
350
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
2
780
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