I'm converting some Java code that uses a Blocking...
# coroutines
t
I'm converting some Java code that uses a BlockingQueue to use coroutines. I was starting to switch to using an actor, but I noticed that it is marked as being ObsoleteCoroutinesApi. What is the replacement? Also, why does the doc recommend this pattern if it is marked Obsolete? https://kotlinlang.org/docs/shared-mutable-state-and-concurrency.html#actors
c
The Actor API itself is obselete, but actors as a general pattern (and how they were used in the obselete
actor { }
API) is basically just
Channels
.
t
I thought Channels were being discouraged as well. I know that they are used internally, but I thought that the use of Channels was discouraged and various Flows were preferred. Is there a recommended alternative?
c
Channels work at a lower level than Flows, and many use-cases are much easier to work with as a Flow vs a channel. Flows are concerned more with how a consumer reads and processes values from the Flow, but doesn’t really care about how values are emitted to it, in general. In contrast, Channels are designed for sending values from one place to another, which is the whole idea of the general Actor pattern. And the two concepts are not exclusive, you can read values from a Channel as a Flow with
channel.receiveAsFlow()
. The Actor pattern, by its nature, needs to be able to send data from one actor to another, and you’d need a Channel to send that data into. Within each individual actor, you could
receiveAsFlow()
and do more advanced filtering/processing of each individual message with the nicer Flow API if needed. But Flows were never designed to replace Channels, and in fact many of the Flow operators and other internal mechanisms are built in top of Channels
t
Thanks for that detailed explanation. I found an old post that explained that the @ObsoleteCoroutinesApi was used because the API was being rewritten. Is that still the case? I don't want write new code that uses obsolete APIs, but maybe that is alright in this case? https://stackoverflow.com/questions/53270280/i-tried-to-use-kotlin-coroutine-channels-and-got-an-obsoletecoroutinesapi-warnin
f
an important thing to know about Channels are that they are fan-out - if you have multiple consumers the events only go to the 1st available consumer, which is the kind of thing you want for the Actor API, as you may be pushing actions to multiple consumers and you only want each action to reach 1 consumer. The deprecated BroadastChannel would change this behaviour.
c
There’s been talk of a new Actor API for several years, but I don’t think there’s been much movement toward it. The Kotlin team hasn’t really started working toward a replacement Actor API/framework, but they haven’t completely abandoned the idea yet either. Depending on where you need to use Actors, I believe I’ve seen JB folks in this Slack saying that it’s basically fine to use the existing Actors APIs because they’re not going to be pulled from the coroutines library anytime soon, though they only work on JVM targets (though definitely don’t quote me on that, evaluate the risk for your own needs). You could also write your own Actor classes that work similarly to the built-in ones to help future-proof yourself at the cost of additional maintenance (here’s a basic example I just threw together to show the main idea).
As @Francesc pointed out, Channels by default send 1 value to be read only once, but you can have multiple coroutines reading from one channel to process each value in parallel. If a single value needs to be read/processed multiple times by different subscribers,
SharedFlow
is intended to replace
BroadcastChannel
.
You might also consider another pattern for concurrency other than strict Actors, such as the MVI model. Without knowing how you are looking to actually use Actors, it’s hard to really say if Actors or MVi is more suited to your use-case, though. MVI is more focused around managing and updating application state for UIs rather than sending messages through a pipeline, but the mental model of MVI and Actors is similar, and there are several good MVI libraries out there to choose from. I maintain Ballast, which supports all Kotlin targets (not just JVM/Android) and just got an update to Kotlin 1.7.10, but I’ve also got a table of other MVI libraries to help you decide if any of those might fit your needs better.
t
In my case, I'm converting some Android code that plays streaming audio. Thanks for all the advice. I appreciate it!