Hi everyone, how do you use coroutines to tackle w...
# coroutines
f
Hi everyone, how do you use coroutines to tackle with the SingleLiveEvent I've tried the code below but when rotating the device the latest value is displayed, i want the value to be consumed only once just as SingleLiveEvent is
Copy code
protected val loadingState = BroadcastChannel<Boolean>(Channel.BUFFERED)
    val actionReceiver = loadingState.asFlow()
s
SharedFlow
is what you want to use
a
If you want only a single consumer to receive something you send, use
Copy code
val loadingChannel = Channel<Boolean>(Channel.BUFFERED)
val loadingFlow = loadingChannel.receiveAsFlow()
send to
loadingChannel
and observe using
loadingFlow
.
f
I tried that but when the rotation happens the last value is emitted
I even tried consumeAsFlow since it happens only once
a
If you use
Channel.receiveAsFlow
then no value will be emitted more than once. Are you sure your other code isn't sending to the channel more than once?
f
I'll check that and post the code if i don't find any solution, I'm literally trying to replace SingleLiveEvent
a
it's good to replace SingleLiveEvent, it's a workaround for a problem that doesn't exist with coroutines/channels/flows 🙂
but SingleLiveEvent encourages some other antipatterns around working with state and events. You may need to resolve those to move forward in an elegant way if they've crept into your codebase.
f
My use case is this User clicks download Download event success or fail (update ui only once) Which I've habdled with single live event before
a
yes, this is where clearly separating state and events helps clarify the design. Events happen only once. State is updated by events, and current state can be observed at any time, as many times as desired. Reacting to state updates is always idempotent, so there's no need to think of it as, "updating UI only once." UI update observers update the UI to match the state with no other side effects. Any one-time side effects of the download complete event should happen as part of observing the one-time download complete event and setting the download completed state used by the UI updaters.
f
In my view model i had val loadingChannel = Channel<Boolean>(Channel.BUFFERED) val loadingFlow = loadingChannel.receiveAsFlow() And in my fragment viewLifecycleOwner.lifecycleScope.launcWhenResumed{ viewModel.loadingFlow.collect{ //Some logic here } } Inside my logic i didn't receive anything I tried loadingChannel.offer(true) // and false Am I doing something wrong?
a
did you check that the
launchWhenResumed
block runs as expected, before the
collect
call begins?
f
Yes it runs since i have another StateFlow collecting the list
Found the problem
message has been deleted
This did not work
whilst this did work
message has been deleted
Copy code
val Fragment.viewCoroutineScope get() = viewLifecycleOwner.lifecycle.coroutineScope
weird, both are suspending functions btw
o
I believe it’s due to
collectSubtitles
is blocking. I bet you start to collect a
Flow
there 🙂
f
yes i did
how do i fix it?
if i use
Copy code
.onEach{}.launchIn(scope)
and put it inside the onResumed block, would it be called in onResume since it's launched in the scope with launchIn?
a
You can wrap each of the two calls that you want to run concurrently in their own
launch
inside a single
launchWhenResumed
Copy code
...launchWhenResumed {
  launch { collectSubtitles() }
  launch { handleDownloadStatus() }
}
f
That's more than i needed, it's clear to me now, thanks Adam 🍺
👍 1
o
But there is no big difference between
Copy code
scope.launchWhenResumed {
   launch { collectSubtitles() }
   launch { handleDownloadStatus() }
}
and
Copy code
scope.launchWhenResumed { collectSubtitles() }
scope.launchWhenResumed { handleDownloadStatus() }
?
a
the former is a bit more efficient
👌 1