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

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.

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):

View.render()

Java

1

2

3

4

5

6

7

8

9

publicvoidrender(ExampleViewState state){

if(state instanceofExampleViewState.Loading){

renderLoading();

}elseif(state instanceofExampleViewState.Data){

renderData((ExampleViewState.Data)state).getData);

}elseif(state instanceofExampleViewState.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):

ViewState

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

interfaceExampleViewState{

finalclassLoadingimplementsExampleViewState{}

finalclassErrorimplementsExampleViewState{

publicThrowable error;

publicError(Throwable error){

this.error=error;

}

}

finalclassDataimplementsExampleViewState{

publicList<RssItem>items;

publicData(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.