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
670
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
140
Building Shared UIs across Platforms with Compose
heyitsmohit
1
540
Building Multiplatform Apps with Compose
heyitsmohit
2
400
Building StateFlows with Jetpack Compose
heyitsmohit
6
1.7k
Building Android Testing Infrastructure
heyitsmohit
1
380
Using Square Workflow for Android & iOS
heyitsmohit
1
370
Building Android Infrastructure Teams at Scale
heyitsmohit
3
280
Strategies for Migrating to Jetpack Compose
heyitsmohit
2
500
Challenges of Building Kotlin Multiplatform Libraries
heyitsmohit
1
390
Other Decks in Programming
See All in Programming
subpath importsで始めるモック生活
10tera
0
310
PHP でアセンブリ言語のように書く技術
memory1994
PRO
1
170
AWS IaCの注目アップデート 2024年10月版
konokenj
3
3.3k
聞き手から登壇者へ: RubyKaigi2024 LTでの初挑戦が 教えてくれた、可能性の星
mikik0
1
130
ActiveSupport::Notifications supporting instrumentation of Rails apps with OpenTelemetry
ymtdzzz
1
230
タクシーアプリ『GO』のリアルタイムデータ分析基盤における機械学習サービスの活用
mot_techtalk
4
1.4k
距離関数を極める! / SESSIONS 2024
gam0022
0
280
Laravel や Symfony で手っ取り早く OpenAPI のドキュメントを作成する
azuki
2
120
Flutterを言い訳にしない!アプリの使い心地改善テクニック5選🔥
kno3a87
1
190
CSC509 Lecture 13
javiergs
PRO
0
110
3 Effective Rules for Using Signals in Angular
manfredsteyer
PRO
0
100
Less waste, more joy, and a lot more green: How Quarkus makes Java better
hollycummins
0
100
Featured
See All Featured
A Tale of Four Properties
chriscoyier
156
23k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
8
890
Automating Front-end Workflow
addyosmani
1366
200k
Designing the Hi-DPI Web
ddemaree
280
34k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
364
24k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
31
2.7k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
126
18k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
The Art of Programming - Codeland 2020
erikaheidi
52
13k
Intergalactic Javascript Robots from Outer Space
tanoku
269
27k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5k
The Power of CSS Pseudo Elements
geoffreycrofte
73
5.3k
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