By Ivan Morgillo, Sasa Sekulic, and Fabrizio Chignoli
This article is an excerpt from Grokking Rx that discusses the basics of state machines, using the example of a radio player.
Save 37% off Grokking Rx with code fccmorgillo at manning.com.
State machine basics
So, what is a state machine? We can define the state machine as an object that:
a) has defined states;
b) has defined transitions between the states (i.e. actions that bring the machine from one state to another).
Figure 1 shows a standard state diagram of a simple state machine with two states and two transitions.
Figure 1: state machine with two states (state 1 and state 2) and two transitions (action 1 and action 2)
Radio player as a state machine
We can present our radio player as a simple state machine. Let’s look at a basic state machine diagram in figure 1: it has two states and two possible actions that change the state from one to another.
Let’s see how we can do this: the stopped state represents the state 1, and the playing state is state 2; actions 1 and 2 are the same action (pressing the play/stop button) but the outcome depends on the player state (playing when it’s stopped and stopping when it’s playing).
Figure 2: player represented with a state machine diagram with two states (stopped and playing) and two transitions (start playing and stop playing)
With a pinch of reactive
How do we implement these two states (stopped, playing) and two transitions (stopped -> playing, playing -> stopped) using reactive programming?
We could store the player state in an
Observable, and to be informed about the changes of the state we could use an
Observer that would subscribe to this
Observable to allow us to react to the changes in the
onNext() method. That way, when the
Observable has a new state, for example
PLAYING, in the
onNext() we could call the method that will start playing the song.
Observable: an object which emits values and events to which an Observer can subscribe. Observer: subscribes to an Observable and receives new events through standardized interface (onNext(), onCompleted(), onError())
But can we do that? Can we send a new state to the Observer?
If you take a closer look at the diagram above, you’ll notice that the Observable has the arrows in the bottom right (which means that it emits the values), and the Observer has them in the top left (which means that it receives the values), but for the above to work, the Observable should be able to also receive the values.
Enter the Subject
Our reactive implementation of the radio player has encountered a problem: we have an
Observable whose value should be updated when we press the button – we need an
Observable creation method that lets us control the
Observable’s value once it’s created.
Creating an Observable
The Observable’s value is decided in the creation methods and we can’t touch the Observable outside of them.
Observable doesn’t do what we need it to do because we can’t change the value at a later point in time, after we’ve created an
Observable. If we want to be able to change the value of the
Observable after its creation, we should use a
Subject is an object that has an
Observable that exposes the
Observer interface through which new values and events can be sent to the
Observable (and received by the Observers subscribed to the
Subject). It behaves both like an observer; it has accessible
onCompleted(), and like an
observable (you can subscribe to it).
What does this mean for us and our radio player?
Now, when the button is pressed, we can call
playerSubject.onNext(NEW_STATE) and the
Observer subscribed to our
Subject receives this new state.
Because this is a simple radio-like player, transitions between the states are executed only when the button is pressed. To make it like an ordinary music player, you can also change the state when the song finishes, and in case of an error call
Look at the updated diagram below, and you’ll notice that our newly placed
Subject can both receive values and emit values to the
This is exactly what we need for our player!
The pieces that we need for our simple player to work are:
- Button, with its associated listener, which checks the current state and sends the new state to the Subject;
- Subject, that receives the new value from the button listener and emits it to its Observers;
- Observer, which contains the business/UI logic of our player, and executes the transition, depending on the state that arrives from the