Model-View-Intent(MVI) in Android

In the last few months, I applied the Model-View-Intent (MVI) architectural pattern to a legacy app. In this blog post I want to describe my experiences with MVI and a comparison between the MVI and MVP (Model-View-Presenter) architectural pattern because MVP is used in Android for quite a while now. But first of all, I am going to show you a quick overview what MVI is about.
Model-View-Intent (MVI)
The architectural pattern Model View Intent was specified by André Staltz for the Javascript framework cycle.js. Besides that it is also suitable for Android or any other UI-based application. The pattern intents to create a readable code base and decouples the UI from the business logic more consistently.

MVI Architectural Pattern
Intent
An intent represents the interaction with the view by the user. The intent delivers changes that need to be processed by the Model.
View
The view, actual the UI, observes and depicts the model’s state. The view forwards each interaction as intent.
Model
Represents more like a state than the original idea of a model. This includes interactions with business logic. The business logic updates the current state and thereby the observing View.
Use in Practice
When I started out applying the MVI pattern, I did not start from scratch. I used the library Mosby which provides you with base classes to build your application upon. Of course you do not need to use a library and can implement a set of base classes yourself. For now the crucial required library is RxJava. That might change in the future with Google’s new Android Architecture Components’ LiveData.
Presenter
Using RxJava the intents get wrapped in an observable which is then subscribed on to react to events emitted by the View. The whole subscription process gets done in a presenter class which only task is to ensure that the observables are connected to the Model layer and return back a state generated by the Model Layer to the View.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class ExamplePresenter extends MviBasePresenter<ExampleView, ExampleViewState> { private final ExampleInteractor interactor; public ExamplePresenter(ExampleInteractor interactor) { this.interactor = interactor; } @Override protected void bindIntents() { Observable<ExampleViewState> intent = intent(ExampleView::itemClick) .flatMap(inputData -> interactor.getData(inputData)) .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()); subscribeViewState(intent, ExampleView::render); } } |
To return this state to the View the component has only one method render(ViewState state)
that gets called with the state that gets created by the Model based on the changes that were applied.

State Interactions
To also handle more than one type of state in one View I check for the different states in render()
like the following (Normally I would not recommend to use an if-else/switch block to differentiate between the different states but in this case it is a good way to show the workflow of the state in the view):
1 2 3 4 5 6 7 8 9 |
public void render(ExampleViewState state) { if (state instanceof ExampleViewState.Loading) { renderLoading(); } else if (state instanceof ExampleViewState.Data) { renderData((ExampleViewState.Data) state).getData); } else if (state instanceof ExampleViewState.Error) { renderError((ExampleViewState.Error) state); } } |
ViewState
The ViewState
is more or less the model in this context. It includes the current data that you want to show and as the state it is always an inner class of ViewState
it also represents the current state the view should change into. An implementation of the different states could look like this (but then again it is up to your personal preference on how you implement your state):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
interface ExampleViewState { final class Loading implements ExampleViewState{} final class Error implements ExampleViewState { public Throwable error; public Error(Throwable error) { this.error = error; } } final class Data implements ExampleViewState { public List<RssItem> items; public Data(List<RssItem> items) { this.items = items; } } } |
Issues
I ran into a few issues using Android Annotations with mosby. The initialization of views with @ViewById
for example is too late for the binding of the observables in the presenter class. The result was that I had to initialize every view which should have a listener in the traditional way with findViewById(R.id.view)
.
Differences between MVI and MVP
Model-View-Presenter (MVP) is a software architectural pattern which is based on Model-View-Controller. It is used to implement user interfaces while keeping the model seperated from the view. The interactions are controlled by the presenter which acts as an mediator between model and View.
Model-View-Intent (MVI) is also a derivation of the original MVC architectural pattern. The pattern fulfills the essential purpose of the original MVC as dicussed by André Staltz in his description of MVI. Instead of working with a proactive controller MVI works with the reactive component called intent.
Model-View-Presenter:
- Seperation between model and view. The presenter acts like a mediator.
- One to one mapping between View and Presenter, with the possibility to use multiple Presenters for more complex Views
Model-View-Intent:
- Unidirectional Flow allows us to work without callbacks between the View and in this case the Intent
- Compulsary Usage of RxJava
- Immutable objects
Should I use MVI or MVP in my project?
Of course this decision is up to you. I would suggest that if you have the time to try out both: Just do it! If you need a starting point, check out Mosby representing a MVP and MVI library. Mosby MVI offers the benefit that RxJava is already established in its library calls and a loose coupling between UI and business logic is inflicted. There is not a lot of documentation about the MVI on Android but its easy to learn.
In my humble opinion, I will probably use MVI in my upcoming apps. The unidirectional workflow makes a lot of things easier to handle, e.g readability, testability, maintainability etc.
If you want to know more in depth about Model-View-Intent in Android I definitely recommend checking out Hannes Dorfmann’s amazing blog series.
References:
- https://cycle.js.org/model-view-intent.html
- http://hannesdorfmann.com/android/mosby3-mvi-1
- http://heim.ifi.uio.no/~trygver/themes/mvc/mvc-index.html
- https://www.infragistics.com/community/blogs/todd_snyder/archive/2007/10/17/mvc-or-mvp-pattern-whats-the-difference.aspx
Portions of this page are reproduced from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.
Comment article
Recent posts






Comments
Sebastian Harner
Thanks for the hint. I fixed it.
Eric Fung
Small typo in first paragraph after MVI: the Javascript library is named cycle.js, not circle.js