25

Kotlin Actors – No Drama Concurrency

 3 years ago
source link: https://handstandsam.com/2020/07/18/kotlin-actors-no-drama-concurrency/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Kotlin Actors are part of the Kotlin Coroutines library. I’ll walk you though the reasons why I use Kotlin Actors to achieve concurrency, while leveraging Coroutines to process reactive events in unknown order.

aY3iEjV.png!web

Concurrency?

  • Allows events to happen out-of-order or in partial order, without affecting the final outcome.
  • This allows for leveraging parallel execution without giving up determinism.

Why Does Android need Reactive Programming?

  • Click Events
  • Intents
  • Networking Requests
  • Disk Writes
  • etc.

Kotlin Coroutines?

Essentially, Kotlin Coroutines are light-weight threads. They are launched in a context of some CoroutineScope.

Kotlin Actors?

  • A Single Kotlin Coroutine
  • Processes incoming Messages
  • Backed by a Channel
  • Concurrent

Actors receive Messages (Intentions) via a Channel

Channels are the only way to safely communicate across Coroutines.

This example implements a Shopping Cart Dao from my GitHub Project ShoppingApp . I’ve created a type called Intention which are sent across the channel as messages. The intentions represent actions I want to perform, but keep my data thread safe.

sealed class Intention {
    class FindByLabel(
        val label: String,
        val deferred: CompletableDeferred<ItemWithQuantity?>
    ) : Intention()

    class Upsert(val itemWithQuantity: ItemWithQuantity) : Intention()

    class Remove(val itemWithQuantity: ItemWithQuantity) : Intention()

    object Empty : Intention()
}

Actors Process Messages Sequentially in a for() loop

These messages (Intentions) come in across a Channel from other Coroutines, get queued, and then get processed by the Actor sequentially to achieve concurrency.

scope.actor<Intention> {
    for (intention in channel) {
        // Process Messages/Intentions
        when (intention) {
            is Intention.FindByLabel -> {
                // ...
            }
            is Intention.Upsert -> {
                // ...
            }
            is Intention.Remove -> {
                // ...
            }
            is Intention.Empty -> {
                // ...
            }
        }
    }
}
MnMzAzN.png!web

Sending Messages to the Actor – send() vs offer()

To send messages to the actor, you send a message to it using actor.send(intention) or actor.offer(intention) . Here are the differences between them ( from the Kotlin documentation of SendChannel ).

Y7rqYzb.png!web

CompletableDeferred to await() Results

We send in messages to the actor, but sometimes we want to wait for a result once the message has been processed by the actor. We use CompletableDeferred to do this. We await() the result, like in this example where we are querying for a value:

class FindByLabel(
    val label: String,
    val deferred: CompletableDeferred<ItemWithQuantity?>
) : Intention()
// ---
val deferred = CompletableDeferred<ItemWithQuantity?>()
actor.send(
    Intention.FindByLabel(
        label = label,
        deferred = deferred
    )
)
val result : ItemWithQuantity? = deferred.await()

Aren’t Actors Marked with @ObsoleteCoroutineApi?

Yes, but complex actors will also support the same use cases, and there will be a clear path. Also, there is no planned replacement at this point. See the response from the Kotlin Coroutines tech lead from the GitHub issue :

R3EBbei.png!web

Slides & Video (Coming Soon)

I was able to present this to Boston Android meetup group and 18 other meetup groups on Tuesday, July 14th which was an amazing experience. The video will be available soon and I’ll be sure to put it here. Here are the slides for now.

Links


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK