https://kotlinlang.org logo
#coroutines
Title
# coroutines
g

gregd

05/13/2020, 2:47 PM
Hi all! I have a question regarding passing
CoroutineScope
. I have a pretty standard Android architecture: ViewModel, Repository, DataSource & API layers. I want to pass
ViewModel.viewModelScope
from the ViewModel to the API layer, BUT I don’t need it in the intermediate layers (Repo & DS). What do I do? First thing that comes to my mind is marking all in-between functions with
suspend
, which would pass the Scope implicitly. But according to @elizarov we should only mark the function with
suspend
if it’s really going to suspend, and those intermediate layers definitely won’t. So, is there a better way? Is there a proper way? How do you do it?
z

Zach Klippenstein (he/him) [MOD]

05/13/2020, 2:53 PM
Why wouldn't your intermediate layers suspend? If your ViewModel talks to your repository, your repository has to talk to your data source and API layers, and if those bottom layers suspend on IO, then the layers above them will need to as well.
b

bezrukov

05/13/2020, 2:53 PM
and those intermediate layers definitely won’t.
Why? Since they will call API (which is suspend), they will be suspend too
g

gregd

05/13/2020, 2:57 PM
Why? It’s due to the rest of our architecture. The repository looks something like the following. This way it’s much easier to handle cases like: multiple outgoing requests per single incoming request.
Copy code
class MyRepository {
    fun handleIncomingRequest(request: Request)
    fun setOutgoingRequestListener(listener: (Request) -> Unit)
}
Just imagine handling such 1:n and n:1 cases with flows/channels. It would be much more complicated imho.
Or am I wrong?
z

Zach Klippenstein (he/him) [MOD]

05/13/2020, 3:15 PM
Callbacks like this and something like Rx/Flow are essentially different ways of expressing the same thing. "Suspension" is really just a different way of saying "callback". If you converted your callback cases to Flows, which I doubt would be that much more complicated, then you'll automatically propagate coroutine context, and you'll also get the benefit of the consistency of using the same language to talk about asynchrony in every layer of your app.
1
g

gregd

05/13/2020, 4:41 PM
@Zach Klippenstein (he/him) [MOD] Maybe I’m stupid, but I’m not able to make such simple 2-way communication pipeline using coroutines without going into really weird stuff (e.g.
channelFlow
)... Meanwhile, callback version is as easy as it gets - you pass a Request one way, and Listener the other way… Can you share a simple Flow-based example?
z

Zach Klippenstein (he/him) [MOD]

05/13/2020, 5:13 PM
I don't think
channelFlow
is "really weird", it's the primary API to create Flows that from callbacks and concurrent sources. Anyway, I'm not saying callbacks are necessarily inherently bad. But to answer your original question, the reason it seems weird that you have to explicitly pass coroutine contexts/scopes around is because you're jumping between different models of expressing asynchrony (coroutines to callbacks and then back to coroutines). If your layers were consistent, then either you wouldn't need to pass scopes around at all (if everything's callback based) or scopes would automatically get passed around (if everything's coroutines). That said, while callbacks are "simpler" in that they involve less code, they also address fewer issues. I.e. error handling, accidentally leaking callbacks, backpressure, are all things you need to solve manually when using callbacks, whereas coroutines have already thought of all this and handle it mostly automatically.
👍 1
1
g

gregd

05/13/2020, 5:39 PM
Believe me, I would rewrite this repository to Flows if it was reasonably easy. But every time I approach this topic I end up with a lot of complicated code, that nobody would fully understand (including me :)). I really think doing something as easy as 2-way communication pipeline should not involve things like
ConflatedBroadcastChannel
etc. Especially Compared to those couple of lines of callbacks, right? So I hope someday I’ll find an easy way, but untill then I’ll probably mark my functions as
suspend
and keep the code as is.
BTW,
StateFlow
looks promising. Not sure if that’s the right direction, but definitely worth trying.
g

gildor

05/14/2020, 2:33 AM
Why ConflatedBroadcastChannel shouldn’t be involved? StateFlow is better becaues of API, but it’s essentially exactly the same, and I really don’t see anything wrong with it. Could you show some example what kind code looks strange for you? I agree with Zack, you have all those problems because of mixing callbacks with coroutines
8 Views