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
900
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
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
340
Building Shared UIs across Platforms with Compose
heyitsmohit
1
730
Building Multiplatform Apps with Compose
heyitsmohit
2
640
Building StateFlows with Jetpack Compose
heyitsmohit
6
2k
Building Android Testing Infrastructure
heyitsmohit
1
660
Using Square Workflow for Android & iOS
heyitsmohit
1
550
Building Android Infrastructure Teams at Scale
heyitsmohit
3
440
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
680
Challenges of Building Kotlin Multiplatform Libraries
heyitsmohit
1
540
Other Decks in Programming
See All in Programming
TSX の <Hoge<Fuga>> という構文に驚いた話 / tsx-type-argument-syntax
kanaru0928
0
240
わからない話を追いかけたら、プログラミング言語を作る側にいた
ydah
3
560
komatsuna「分散システムにおけるバグ分析手法」
komatsunaqa
0
290
ドリフトを絶対に許さない(?)CDK運用 / CDK Ops with Zero Tolerance for Drifts (?)
akihisaikeda
1
240
Press start. Python's next generation.
willingc
PRO
3
250
Jindong: Introducing Declarative Haptics in Compose Multiplatform
l2hyunwoo
0
130
バグを直したら useEffect が消えた
colorful12
3
770
源内ハンズオン概要編
hideg
0
220
FastAPI の並行処理モデルを完全に理解する
hoto17296
8
3.2k
AIに既存システムを理解させる技術 ~レガシーを見捨てないハーネスエンジニアリング入門~
ochtum
0
170
ソフトウェアエンジニアにとっての生成AI - 特性を知って使い倒す / generative ai for software enginner
kishida
7
2.2k
書籍「プロフェッショナルAI駆動開発」紹介スライド
juntaromatsumoto
0
780
Featured
See All Featured
Google's AI Overviews - The New Search
badams
0
1.6k
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
Utilizing Notion as your number one productivity tool
mfonobong
4
560
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
500
Game over? The fight for quality and originality in the time of robots
wayneb77
1
260
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
Building Flexible Design Systems
yeseniaperezcruz
330
41k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
220
How to Align SEO within the Product Triangle To Get Buy-In & Support - #RIMC
aleyda
2
1.8k
The agentic SEO stack - context over prompts
schlessera
0
880
Typedesign – Prime Four
hannesfritz
42
3.1k
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