How to switch between different Akka Actor states with “become”

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 13.11, “How to switch between different Akka Actor states with become”.

Problem

In a Scala application, uou want a simple mechanism to allow an Akka actor to switch between the different states it can be in at different times.

Solution

Use the Akka “become” approach. To do this, first define the different possible states the actor can be in. Then, in the actor’s receive method, switch between the different states based on the messages it receives.

The following example shows how the actor named DavidBanner might switch between its normalState and its angryState (when he becomes The Hulk):

package actortests.becometest

import akka.actor._

case object ActNormalMessage
case object TryToFindSolution
case object BadGuysMakeMeAngry

class DavidBanner extends Actor {
    import context._
    def angryState: Receive = {
        case ActNormalMessage =>
             println("Phew, I'm back to being David.")
             become(normalState)
    }
    def normalState: Receive = {
        case TryToFindSolution =>
             println("Looking for solution to my problem ...")
        case BadGuysMakeMeAngry =>
             println("I'm getting angry...")
             become(angryState)
    }
    def receive = {
        case BadGuysMakeMeAngry => become(angryState)
        case ActNormalMessage => become(normalState)
    }
}

object BecomeHulkExample extends App {
    val system = ActorSystem("BecomeHulkExample")

    val davidBanner = system.actorOf(Props[DavidBanner], name = "DavidBanner")

    davidBanner ! ActNormalMessage // init to normalState
    davidBanner ! TryToFindSolution
    davidBanner ! BadGuysMakeMeAngry

    Thread.sleep(1000)
    davidBanner ! ActNormalMessage

    system.shutdown
}

Here’s a description of the code:

  1. The davidBanner actor instance is created as shown in previous recipes.
  2. The davidBanner instance is sent the ActNormalMessage to set an initial state.
  3. After sending davidBanner a TryToFindSolution message, it sends a BadGuysMakeMeAngry message.
  4. When davidBanner receives the BadGuysMakeMeAngry message, it uses become to switch to the angryState.
  5. In the angryState the only message davidBanner can process is the ActNormalMessage. (In the real world, er, entertainment world, it should be programmed to receive other messages, like SmashThings.)
  6. When davidBanner receives the final ActNormalMessage, it switches back to the normalState, again using the become method.

Discussion

As shown, the general recipe for using the become approach to switch between different possible states is:

  • Define the different possible states, such as the normalState and angryState.
  • Define the receive method in the actor to switch to the different states based on the messages it can receive. As shown in the example, this is handled with a match expression.

It’s important to note that the different states can only receive the messages they’re programmed for, and those messages can be different in the different states. For instance, the normalState responds to the messages TryToFindSolution and BadGuysMakeMeAngry, but the angryState can only respond to the ActNormalMessage.

See Also