Implementing State Machine in Android App

Yolanda Septiana
3 min readJun 3, 2020

--

I’m sure you’ve all seen this type of code:

This actually doesn’t look so bad, but as your branches grow, it will add cognitive load each time you read the lines. Also, what happens when you write this?

if (isLoading && errorMessage != null)

That doesn’t even make sense and is actually an invalid case, i.e. your app does not handle this, but it is writable and nothing can invalidate it.

One tool that can help you maintain this type of complexity is a state machine. There are many ways to implement a state machine in your code, starting with simple custom state machines to formal state machines that accept states and inputs.

What is a state machine?

If you took a course about Automata in college, this should be familiar. It is a computational model that consists of states and transition functions. A transition function reads an input and determine the next state. A program cannot be in two states at any given time.

Let’s start with an example: fetching data from an endpoint. The program can have 3 states: loading, success, and error. A state machine also has a starting state, which coincidentally here is named “not started” but you can name it anything you want. Here, the starting state indicates that the API is not called yet.

State machine for network API call

When a user opens a screen, the screen begins fetching the data, so the state moves to “loading”. If the API call returns success/error, the state moves to SUCCESS/ERROR respectively. After that, if the user refreshes the screen, the state goes back to LOADING. We could call the annotation “fetch”, “error”, “success”, “refresh” an event or input. The transition function receives the current state and an input and determines the next state.

Any meaningful program always has side-effects. These side-effects are the result of transition from state A to B. For example, when the state changes from NOT_STARTED to LOADING via the event fetch, the program shows progress bar to the screen and fetches some data from the server.

Let’s dive into the code. First, we define the states (when drawing the diagram you should also start with defining the states). The ERROR and SUCCESS states have payloads. We also use sealed class to make sure that the states are all in one place.

Next, we build the state machine. It has the current state and several transition functions. I build the transition functions as literally functions because this is just a small machine, so I’m okay with this. You might want to implement it the right way, though. Remember, a transition function accepts a state and an input.

The transition function also validates the event. For example, if the current state is not LOADING, then the call to success() wouldn’t change anything. Typically you would want to do something when the current state changes from A to B, it is called side-effects. In the above code, I put a callback function onStateChanged that will be invoked after every state change.

Finally below is an example of a network request in a screen. When the state is LOADING , data will be fetched and a loading view is shown. The function fetchData will call success() or error() based on the API call result.

Libraries

This library from Tinder is worth looking around:

A whole category of Finite State Machine libraries on Android-Arsenal:

Special thanks for Stefanus Anggara for correcting things, and Stephanus Jeffry for telling me another use case :)

--

--