MvRx Android on Autopilot: Getting Started

In this MvRx Android tutorial, you’ll learn how to use this pattern to render the screens of your app based on ViewModels that change state. By Subhrajyoti Sen.

Leave a rating/review
Download materials
Save for later
Share

MvRx, pronounced “Mavericks,” is an Android framework from Airbnb. It makes it easy to build simple and complex screens for your apps. It’s based on Model-View-ViewModel architecture and is a Kotlin-first and Kotlin-only framework.

In this tutorial, you’ll learn about the core concepts of MvRx, how to implement them and how they come together to build an awesome app.

You’ll do this by creating an app that displays a list of movies and lets the user add them to a watchlist. The user can then open the watchlist to see the movies they’ve added. They can also remove films from the watchlist.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.

Inside the materials, you’ll find a starter project and a final project. Open Android Studio and select the Open an existing Android Studio project option.


Open an existing Android Studio project

Select starter-project from the download materials folder.

Select the starter project from the download materials folder

After you’ve imported the starter project, build and run. You’ll see a screen like this:

starter project initial screen

Right now, the ProgressBar is indefinite. Later in the tutorial, you’ll hide it when data loads successfully.

Clicking on the menu option in the toolbar doesn’t do anything right now. Later in the tutorial, you’ll modify it to open a new page that shows your watchlisted movies.

The starter project consists of the following files:

  • MainActivity.kt only contains fragments.
  • AllMoviesFragment.kt displays all available movies.
  • WatchlistFragment.kt displays the watchlisted movies.
  • MovieModel.kt represents the properties of a movie.
  • WatchlistRepository.kt fetches a list of movies. It simulates a network call and provides a mocked list of 10 movies after a delay of three seconds.
  • MovieAdapter.kt displays and lets the user interact with the movie list.
  • WatchlistApp.kt contains objects users can use throughout the app.
  • SplashScreenActivity.kt displays the splashscreen.

Now that you’re familiar with the materials you’ll be working with throughout this tutorial, it’s time for some background on what MVRX is and how it helps you as a developer.

Why Use MvRx?

Before you dive into an entire tutorial on how to use MvRx, take a moment to consider its advantages and whether it’s right for your project.

MvRx offers:

State management refers to the task of retaining the state across various conditions and changing from one state to another.

MvRx makes state management easy and flexible to work with.

  • State management: The state of your app is a data structure that can represent the app’s properties at any given time. For example, in a social media app, the state could contain the user’s information and the list of recent posts.
  • Integration with Android Architecture components: MvRx is built on top of Android Architecture Components like ViewModel and Lifecycle. This makes it easy to incrementally adopt in projects where you’re already using architecture components.
  • Conceptually based on React: React is a popular web framework that helps build reactive apps. MvRx brings the concepts of React to Android so you can take advantage of its features.

So how does MvRx work? You’ll take a look at its core functionality next.

Understanding MvRX’s Core Concepts

MvRx has four main concepts:

  1. State
  2. ViewModel
  3. View
  4. Async

Here’s a deeper look at what each of these does.

State

State is an immutable Kotlin data class that contains all of the properties necessary to represent your screen. In other words, it represents the state of your app – no pun intended. It needs to be immutable so you can safely access it from different threads. The only way to modify the state is to use the copy() operator on it. Every state class should implement MvRxState.

ViewModel

MvRx uses a class called MvRxViewModel that extends Google’s ViewModel class.

The main difference is that MvRxViewModel depends on a single immutable MvRxState instance. It doesn’t rely on LiveData to notify changes. MvRxViewModel contains methods that can modify the state; the view can use only these functions to modify the state.

You need to construct MvRxViewModel with an initial instance of MvRxState. If all the values of MvRxState have default values, MvRx will create a default instance and use it.

View

The user sees and interacts with the UI called the view, which can be a fragment or an activity. A view that needs to observe the state changes must extend the MvRxView interface.

MvRxView has a method called invalidate() that the view must implement. Whenever any property of the state changes, it calls invalidate() and updates the view.

The view can access the state using MvRxViewModel as follows:

withState(viewModel) { state ->
  ...
}

Async

Async is a wrapper class that makes it easy to deal with asynchronous sources. Async allows your state’s properties to exist in different states, depending on the ViewModelState.

For example, you might make a network request that takes a few seconds to make data available to you. While it loads, you want to show a loading symbol in the UI. To implement this, you can use a variable that stores whether the data is currently loading and changes its value when the data loads.

Now, imagine that an error occurs during the request. So, you store the error data in a separate variable.

Although this approach might work, it gets complicated when there are multiple asynchronous requests and you have to find a way to handle the errors separately.

That’s where Async comes in. It uses types to handle this information more smoothly.

Async has four types:

  1. Uninitialized: Represents that there’s not a field value yet. Use this type when you want to perform an action only if the field has a value.
  2. Loading: Represents that the field’s value is loading. This is useful when you want to show a progress bar or loading icon.
  3. Success: Use when the data has successfully loaded to stop your loading symbol and carry on with the rest of the app’s operations. This type has a property called value that contains the actual data you need.
  4. Fail: Indicates that an error occurred in the request so you can show an error UI. This type has a property called error that provides an exception with the error message. Use this to perform different actions depending on the error type, or just log the exception.

Here is an example of the different types in use:

val wordsOfTheDay: Async<List<String>> = Uninitialized
// make a network call
wordsOfTheDay = Loading
// network call succeeds
wordsOfTheDay = Sucess(resultOfNetworkCall)
println(wordsOfTheDay()) // prints the words of the day

You are now going to build the app. You will start by creating the State.