Was exploring stream api's in co-routine. I have a...
# coroutines
r
Was exploring stream api's in co-routine. I have a use-case where, i want to logout from app once the auth sdk, i am dependent on emits some event. In auth sdk, i have a created a channel
val channel = Channel<Boolen>
which emit a event when certain action happens and in the consumer application, i have this code sitting there in
BaseActivity
Copy code
lifecycleScope.launch {
   repeatOnLifecycle(Lifecycle.State.STARTED) {
     if(AuthSDK.channel.receive){
       // logout from app
     }
   }
}
It works great, i was wondering if it's the correct way of doing it or if there's a better solution in co-routine stream world. • Reason why i didn't go with
SharedFlow
- events can be lost when producer produces but consumer moves to STOPPED state. • Current channel i am using is
RENDEZVOUS
channels, in this scenario, there is a guarantee for my event to be received and emitted, irrespective of lifecycle changes. • I realised instead of
receive
,
consumeAsFlow
or
receiveAsFlow
can be used. As this is one-off event does it matter if don't. • Both sender and receiver channel co-routines are on main thread, that also means, both
send
and
receive
will be in suspended state. Is it a concern, is there a trade-off in doing so.
I realised
repeatOnLifecycle(Lifecycle.State.STARTED)
invoked late, as it waits for the lifecycle to be in
STARTED
state from
STOPPED
state and now, it looks like
Copy code
lifecycleScope.launch {
   repeatOnLifecycle(Lifecycle.State.CREATED) {
     if(AuthSDK.channel.receive){
       // logout from app
     }
   }
}
n
• Reason why i didn't go with
SharedFlow
- events can be lost when producer produces but consumer moves to STOPPED state.
Not if you leverage the replay cache. For this scenario, I'd probably just use a StateFlow. Whether we should be logged in or not seems like state to me. StateFlow is basically just SharedFlow with replay=1 (and dedupping behavior). You won't miss the event either way.
• Current channel i am using is
RENDEZVOUS
channels, in this scenario, there is a guarantee for my event to be received and emitted, irrespective of lifecycle changes.
RENDEZVOUS
is not like
CoroutineStart.Atomic
. The Activity could be destroyed between receive being scheduled and receive actually resuming. In this case, the item is dropped. This is part of the "Prompt cancellation guarantee" described in Channel. If you want this to happen "irrespective of lifecycle changes" then why are you using
repeatOnLifecycle
?
• Both sender and receiver channel co-routines are on main thread, that also means, both
send
and
receive
will be in suspended state. Is it a concern, is there a trade-off in doing so.
Producer and consumer sharing a thread only causes problems if you block. Coroutines are designed to share a single thread without issues as long as you are suspending, not blocking. Actually,
send
is unlikely to ever suspend. It'll see there's a receiver and tell it to resume, which will schedule receive to pick up where it left off. Even with
Main.immediate
, I don't think it'll resume immediately since you are calling
send
so you are already in a coroutine.
r
Thanks @Nick Allen, i would have gone with
StateFlow
but it leverages replay cache, as in it will keep the last item always in the cache (as you mentioned a special case of sharedFlow with replayCache set to 1) and any new collector will get it. It could be solved, by changing it's state once i consume it. But, i want to avoid this solution, where theres a two way communication to make sure that the event is received only once, by notifying that event is consumed.
The Activity could be destroyed between receive being scheduled and receive actually resuming. In this case, the item is dropped. This is part of the "Prompt cancellation guarantee" described in
I came across this discussion - i believe both send and receive if posted on
Main.Immediate
the event will be collected, irrespective of prompt cancellation https://github.com/Kotlin/kotlinx.coroutines/issues/2886#issuecomment-901188295
If you want this to happen "irrespective of lifecycle changes" then why are you using
repeatOnLifecycle
?
Thanks for pointing it out. I agree
repeatOnLifecycle
can be avoided.