Android MVI Architectural Pattern

Rizkyalfikri
7 min readNov 21, 2021

Over the last decade, smartphones have revolutionized our world. More consumers are using smartphones for different activities than ever before. With the increasing number of Android smartphone users, there is a demand to build applications that can be used by multiple users. Building scalable applications is not easy. As we develop apps, we start facing new challenges and issues. Fortunately there is Android Architecture Pattern to guide us to avoid issue or mistake that happen by past developer when building app.

Android Architecture Patterns are blueprint with a set of rules to follow. These patterns evolved through the mistakes done while coding over years. New patterns will be discovered as we keep solving similar challenges. As Android Developers, we have MVC, MVP, and MVVM as the most commonly used patterns. All of them use an imperative programming approach. With this approach even though most of our challenges will be resolved, we still face some challenges regarding the thread safety, maintaining states of the application. The latest addition to these patterns is Model View Intent(MVI) and this article I will talk about this architecture.

What’s MVI

MVI is architectural pattern that recently introduced in Android, its similar like other architecture pattern like MVP or MVVM where there is two new concept called intent and state. Intent is event that triggered from user and send to ViewModel or Presenter in order to perform process or task. In the end the output from that process will produce new data that will send to view to updating UI. We can call this new data with state. The MVI work principle is like Unidirectional Data Flow, it means that data has one, and only one way to be transferred to other parts of the application where this flow inspired by the Cycle.js framework.

MVI consist of three components. ModelViewIntent

Intent: This component represent action that triggered by user at View component or by the system it self with a desired result. This Component take the input from user that will be passed as input to Model component. Intent could have value like simple string or more complex data structure to set value or manipulate Model component. Also Intent in here is different with intent in Android component.

Model: This component represent the the state of UI, for example UI might have different states like Idle, Loading, Data Loaded, Error, etc. This component take output from Intent as input to manipulate model. The output of this component is new model / state.

View: Same like view in other architectural pattern. This component can be represent as Activity or Fragment that observe state from Model component to updating current UI state or observe intent / event that triggered by user.

Unidirectional Data Flow

Advantages and Disadvantages of MVI

Let’s see what are the advantages and disadvantages of MVI

Advantages of MVI

  • Maintaining state is no more a challenge with this architecture, As it focuses mainly on states.
  • As it is unidirectional, Data flow can be tracked and predicted easily.
  • It ensures thread safety as the state objects are immutable.
  • Easy to debug, As we know the state of the object when the error occurred.
  • It’s more decoupled as each component fulfills its own responsibility.
  • Testing the app also will be easier as we can map the business logic for each state.

Disadvantages of MVI

  • It leads to lots of boilerplate code as we have to maintain a state for each user action.
  • As we know it has to create lots of objects for all the states. This makes it too costly for app memory management.
  • Handling alert states might be challenging while we handle configuration changes. For example, if there is no internet we will show the snackbar, On configuration change, it shows the snackbar again as its the state of the intent. In terms of usability, this has to be handled.

Creating Project

Ok talk is cheap, let’s build the project. We will create GameDatabseApp with using https://api.rawg.io api. So if you still not yet use that api, please take look the documentation because you need registration to get api key.

Overall our project will have architecture like this.

Project Structure

Also in the end our app will like this.

Demo App

Create a Project

  • Start a new Android project
  • Select Empty Activity
  • Name: GameDatabaseMviApp
  • Package name: for the package name it’s up to you
  • Language: Kotlin
  • Finish
  • Your starting project is ready now

Add Dependencies

Let’s add dependencies in build.gradle file at App level

Dependencies in build.gradle file at App level

Also don’t forgot to activate ViewBinding feature

Activate ViewBidning at build.gradle file at App level

Now first let’s create packages to organize our Kotlin file.

Structure Project

Add two models class at package data -> model, these class will hold data from response api.

GameResponseModel.kt
GameInfoModel.kt

Now create network service file at package data -> network. Create a class ApiService.kt to config HTTP methods to communicate to the API.

ApiService.kt

Also create object RetrofitService.kt where we will build endpoint URL and consume REST services.

RetrofitService.kt

We have already config network service, but before we implement network service to get data from api, let’s create a State class where this class can wrap data and will also tell the View how and what the UI should look like. Create MainState.kt file at package state.

There are five different state, that will inform View where it will handle Loading, Idle, Empty, Failed, and Success UI.

Now let’s create class that will implement network service. Go to package data -> datasource and create Interface file with name NetworkRepository.kt and then create NetworkRepositoryImpl.kt .

NetworkRepository.kt
NetworkRepositoryImpl.kt

As we can see, we create NetworkRepositoryImpl.kt that implement NetworkRepository.kt with parameter constructor ApiService.kt . Inside of searchGames method, we request response data to API with parameter apiKey ( you will get this api key from registration at https://api.rawg.io) and queryGame. After that we mapping each conditions request, when we got success request with empty/null data or exist also we handle when failed request happen. In the end, we wrap the whole process with flow function and since this request is calling to API that will blocking our UI. We use Dispatchers.IO, so this process isn’t executed in Main Thread

For the next let’s create MainRepository.kt in data package. In here with parameter constructor NetworkRepisoty.kt, we just call searchGames method from it which will later be used by ViewModel class.

MainRepository.kt

Then create a Intent class where this class can be an action that triggered by user and also that can wrap data. Create MainState.kt file at intent package.

MainIntent.kt

We have already create intent and state class also configure logic to fetch data from API. Now let’s consume it at UI layer, create MainViewModel.kt at ui package.

MainViewModel.kt

As we can see above, MainViewModel.kt have constructor parameter MainRepository.kt . Property queryGameIntent will be observe intent / action from user with initial state Idleand also queryGameState property will observer response from requesting data to API where that response will later be consumed by View. In initialize scoper, we call handleIntent() method, this method consume queryGameIntent property. If intent is MainIntent.FetchGames then call searchGame() method with parameter that carried by FetchGames Intent. At searchGame() method, we mapping queryGame parameter. If parameter is empty String then we set queryGameStateto Idle, but if not we set to Loading also call searchGame() at class MainRepository.kt and then observer the result from it. Once we got the result, set queryGameStateto it.

Don’t forget to create ViewModelFactory.kt class. This class will help us to inject MainRepository parameter to MainViewModel class.

ViewModelFactory.kt

Next create GamesAdapter.kt class to display data in list.

GamesAdapter.kt

Now, let’s set up the XML layout.

In the layout folder, update the activity_main.xml with the following code.

Add item_layout.xml in the layout folder and add the following code,

item_layout.xml

Add the following in strings.xml.

strings.xml

Let’s see our MainActivity.kt, This activity will takes input from the user, based on this MVI checks for the states mentioned in MainViewModel.kt and loads the particular state in the view.

Let’s see how our MainActivity.kt takes care of requesting data, handling states

MainActivity.kt

Here, we are sending the intent to fetch the data on each user typing at search form (User Action).

Also, we are observing on the MainViewModel.kt state for the state changes. And, using “when” condition we are comparing the response intent with Loading state, Idle state, Empty state, Failed state, and Success state. We have handled each state differently.

Last, don’t forgot to enable internet usage at AndroidManifest.xml file.

AndroidManifest.xml

In the end our structural packages and files for our project will like this

Structure Project

Then let’s run our project.

Demo App

Congratz, we already MVI pattern on our Android Project and that’s it we are at the end.

Conclusion

MVI is an architectural design pattern based on reactive programming.
The goal is to have less complex code, testable and easy to maintain. The MVI work principle is like Unidirectional Data Flow, it means that data has one, and only one way to be transferred to other parts of the application.
An Intent describes the action that triggered by user or system it self.
State is data that produces by process where that process is triggered by Intent.

You can see and download completed project in here

Thank you for taking your time to read this long post.

--

--