Hi, are actors to be used with flows? or are they ...
# coroutines
u
Hi, are actors to be used with flows? or are they different paradigm? Im trying to move my rx stuff to flows. translates easily. But I hear actors are nice in a way to bound paralelism (downloader queue) which Id find handy. But I see examples only actors receving and sendign to another actors, which leads me to believe thata its different abstraction alterantive to flows?
p
They are 2 different concepts. Research for the Actor model or Actor Programming paradigm outside of kotlin. Flow is more related to reactive/functional programming paradigm. In reactive you create pipes of computations. Each computation has an input and an output. Like a function in math. A pipe is a chain made of these functions. Or in reactive terms you have a source and a sink. The combination of both can be said is a Flow. Other libraries used different terms. To migrate RxJava code, Flow is the more straightforward way. Actors are for handling Shared mutable state in a maximum lock-free way.
The cool thing about kotlin coroutine is that you can mix both apis really well.
u
Okay thanks thought so. So If I use Flow for reactive stream, yet I want a regular sequential suspend download function, which should work on IO dispatcher in parallel but at most say X parallel downloads - do I want an actor or something manual to limit it? (given I dont yse actors anywhere)
p
You can limit IO operations with both APIs. With Flow: - You can use
flowOn
to specify a custom
CoroutineContext
where you want the Flows to execute on. Setup this
CoroutineContext
with a custom
Dispatcher
that has a max of X threads. Check the Default Dispatcher or
asCoroutineDispatcher
to create one from an ExecutorService if the Default don't work for you. With Actor: Actors will give you more grained control since you can "manually" handle your request. You can also optimize for
Inflight Requests
to a same resource avoiding
n
parallel calls to the same remote resource. - This solution needs more code. An Actor is an interesting Abstract concept but in practicality is a Queue receiving events and processing them in sequence. That you can achieve wrapping a Channel. Then to handle the event stream use
channel.consumeEach{... your lambda ...}
You can still use
actorOf
builder present in the framework but don't know if you are going to like the Experimental/Obsolete tag that has on it. For the first approach to mimic an Actor check this: https://github.com/pablichjenkov/Android-Actor/blob/master/app/src/main/java/com/hamperapp/actor/Actor.kt
u
Yes, that first approach you mention is what I do in rx, i.e. a fixed thread count pool. But, I was thinking if it made sense to have this use IO dispatcher, so threads are shared, and limitation is logical not "physical"
Since that would allocate way 5 threads for nothing right?
p
If you are really concern about performance and don't want to have an extra Executor sitting in RAM holding some warmup threads. Then you better do manual control of the requests, and as you said you share only a common IO Executor. You might not even need an Actor, you can write this request control logic in the Service/Manager that expose the Flow pipe to clients. Although, taking this route will probably end up writing mutable shared logic. You will need to count how many clients have collected Flows and so on, not advisable.
u
Yes, btw on Flow.flatMapMerge there is maxConcurrency param which might be used for this
Maybe if I refactor this to straight suspend functions, no flow, no actors, would it help me somehow? while riding on top of IO dispatcher
or by control logic did you mean some manual blockingqueue?
p
Haven't used
flatMapMerge
, I have no idea what that param is for. By Request Control Logic I mean putting code in your Remote Data Manager class to limit the amount of ongoing request. A BlockingQueue will be needed to put incoming exceeded requests on hold while the current ones complete. There is probably something like that already done but less likely in Kotlin. If you are using ok-http you can setup its internal executor. Again Executors are well optimized to minimize resource consumption when there is no work to do. So having a couple of them even in low end devices won't hurt.
u
Yes thats exactly what I meant by manual control logic
So your saying you'd do Executor.newFixedBlabla(n).asCoroutineDispatcher() and call it a day?
p
That and don't forget the
flowOn
operator. I honestly prefer what we have talked about the manual control. That solution abstracts the underlaying machinery. Although requires more time to implement. It would be interesting doing so. Okhttp do some similar concept internally to limit the amount of concurrent Calls. https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/Dispatcher.kt They based their implementaion on the fact that an
AsyncCall
has a reference to the dispatcher. So when job completes on the AsyncCall it let the dispatcher knows and it keeps pulling from the AsyncCall queue the next Task. Have no clue on how to make an equivalent using the Flow API. 🤔 let me think.