Mobius Tutorial for Android: Getting Started

Learn about Mobius, a functional reactive framework for managing state evolution and side effects and see how to connect it to your Android UIs. By Massimo Carli.

Leave a rating/review
Download materials
Save for later
Share

Choosing the proper architecture for your project is one of the most important steps in the development process. It impacts the maintainability and testability of the software you create and, therefore, its cost. In this tutorial, you’ll learn about Mobius, a unidirectional architecture defined as:

A functional reactive framework for managing state evolution and side-effects, with add-ons for connecting to Android UIs and RxJava Observables. It emphasizes separation of concerns, testability, and isolating stateful parts of the code.

A unidirectional architecture is simple because, as the name says, it allows the data to flow in a single direction from a single source of truth to the UI.

Note: If you want to learn more about the available architectures in Android, Real-World Android by Tutorials and Advanced Android App Architecture are the right books for you.

This definition contains some important functional programming concepts you’ll learn in this tutorial, along with:

  • What the Mobius loop is and how it works.
  • What the Mobius workflow is and how to apply it to a real case.
  • How Mobius handles side effects.
  • How Mobius works with Android.

You’ll do this by creating a simple Mobius version of the popular game Memory. :]

Note: The app in this tutorial uses Dagger and Hilt for dependency injection and Jetpack Compose for the UI. If you want to learn all about the Android dependency injection framework, Dagger by Tutorials is the perfect book for you. By reading Jetpack Compose by Tutorials, you’ll learn everything you need to know about Jetpack Compose.

Getting Started

Download the materials for this tutorial using the Download Materials button at the top or bottom of this page. Open the project using Android Studio 2021.1.1 or above. Here’s the structure of the project:

Mobius Project Structure

Mobius Project Structure

Mobius Project Structure

The project has a very simple structure. In the previous image, you see three main packages:

  • di: with MobiusModule with all the main bindings Dagger needs.
  • mobius: with the Mobius-specific code for the app. This is where you’ll write most of the code.
  • ui: contains the UI code with composable functions and some reusable visual components.

Build and run the app, and after a splash screen, you’ll get the following:

Menu Screen

Menu Screen with PLAY and CREDITS buttons

Menu Screen

If you click the PLAY or CREDITS buttons, nothing happens because you need to add some code. Before that, you need to understand how Mobius works and what it means to implement an application with this framework.

Understanding Mobius Principles and Concepts

To understand how Mobius works, just think about a typical mobile app. You have some UI that displays information. You usually interact with the UI by pressing buttons or providing input. This triggers actions to access, for instance, a server, fetch some data and show it in the UI. This might look like an overly simplified description of what usually happens, but the reality isn’t far off. You can represent the flow like this:

The Mobius Loop

The Mobius Loop

The Mobius Loop

This image has numerous interesting concepts you can understand by following the flow described earlier.

When you launch your app, you see a UI, which is usually a composition of views, like TextViews, Buttons and so on. You can think of a view as a way to represent data. When the data changes, the UI usually changes. This is important because you can think of the data as the UI’s current state. The data you want to display with the UI is usually represented as the model.

Think about the app’s initial screen, which is the main menu. If what you see is the visual representation of a model, the model will probably contain a property that says what the current screen is. Rendering a screen in place of another with Jetpack Compose will simply consist of executing one composable function instead of another based on that property of the model.

As mentioned earlier, the user interacts with the UI by pressing buttons or providing data as input. Mobius models these actions as events. An event is what makes an app interesting. Some events just update the UI, creating a new model to display. Others are more complicated because they trigger a request to the server or access a database. Using the same navigation example, tapping the PLAY or CREDITS buttons will trigger an event that changes the property of the model representing the current state, and so, the UI.

Mobius Update Function

To handle both use cases, Mobius provides an Update function. It receives the current model and the input event, and returns the new model and an optional description of a side effect. It’s crucial to see how the Update function lives in the Pure section of the diagram. The Update function is pure because:

  • The new model just depends on the input model/state and event.
  • It returns a description of the side effects you need to execute eventually.

This makes the Update function very easy to test. Not represented in the image is the Init function. Init is a version of Update that generates the first state and, optionally, the first set of effects to generate.

Now, Mobius sends the new model to the UI and the optional effects to some effect handlers. These are what actually execute the side effects, usually in a background thread. It’s also interesting to see that effect handlers notify the outcome using events and how they go through the same flow you saw earlier for the events from the UI.

The Update function is invoked, and a new state/model is created along with other optional side effect descriptions.

Finally, an event source represents a generic component able to generate events even without a specific interaction from the user. Think about the events related to the battery level or your device going offline and then back online again.

The previous architecture is straightforward and gives each component a clear responsibility, as the separation of concerns principle suggests. This also allows the implementation of a process named the Mobius workflow.