Android MVI Architectural Pattern

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. Model — View — Intent
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.

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.

Also in the end our app will like this.

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
Also don’t forgot to activate ViewBinding feature
Now first let’s create packages to organize our Kotlin file.

Add two models class at package data -> model, these class will hold data from response api.
Now create network service file at package data -> network. Create a class ApiService.kt
to config HTTP methods to communicate to the API.
Also create object RetrofitService.kt
where we will build endpoint URL and consume REST services.
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
.
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.
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.
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.
As we can see above, MainViewModel.kt
have constructor parameter MainRepository.kt
. Property queryGameIntent
will be observe intent / action from user with initial state Idle
and 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 queryGameState
to 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 queryGameState
to it.
Don’t forget to create ViewModelFactory.kt
class. This class will help us to inject MainRepository
parameter to MainViewModel
class.
Next create GamesAdapter.kt
class to display data in list.
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
.
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
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
.
In the end our structural packages and files for our project will like this

Then let’s run our project.

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.