Evolution of Android Architectures — MVP, MVVM, MVI

Gaurav Arora
MindOrks
Published in
5 min readJul 22, 2019

Imagine a world where every developer codes the way he feels like — with no guidelines, principles or rules. The end result would be a code that would be difficult to comprehend, probably buggy & definitely a nightmare to maintain.

Architectures are what brings consistency to the world of software engineering. They serve as high level guidelines or rules for designing your applications. Making developers write in a proven consistent way that is easily understandable, scalable & less prone to errors.

So, now that you are sold on why architectures are important. Let’s dive into details of commonly used Android architectures.

The Beginner: MVP

Model View Presenter (MVP) was one of the first few mainstream architecture adopted by the Android community.

What was the need?

Before MVP became mainstream, most of the developers wrote their views & business logic in Fragments or activities. It brought about multiple problems. First, developers were more prone to using activities & fragments as “God” classes. Second, the business logic & views were tightly coupled with no clear separation of concerns. Third, all this led to a code which wasn’t easy to unit test.

This drove the need to segregate View & Presentational logic.

Components in MVP

  • Model: Source of data. Comprises of repositories & services.
  • View: The UI layer.
  • Presenter: Handles presentational logic. Interacts with model & updates view.

Example: Consider a basic app where you want to load a feed view & display in UI. This is how your various components will look like:

Model:

interface Repository{
fun getFeed(): Single<List<Post>>
}

View:

interface View { 
fun showLoader()
fun hideLoader()
fun showFeed(feed: List<Post>)
}

Presenter:

interface Presenter {
fun loadFeed()
fun attachView(view: View)
fun detachView()
}

Problems that we solved:

  • Better separation of concerns: UI & presentational logic are now decoupled
  • Presentation logic can be individually unit tested
  • Code reusability: Single view can be used across multiple presenters

The Reactive: MVVM

In 2015-16, RxJava saw a huge adoption in Android community & developers absolutely loved it. Soon, reactive concepts became highly popular among Android devs & developers started working with reactive data streams. The reactive patterns later paved way for adoption of MVVM in Android.

The core idea behind MVVM is that the current state of the views is represented by a model. The views subscribe to this model for any changes. And to update a view, you simply update this model.

Components

  • Model: Source of data. Comprises of repositories & services.
  • View: UI layer. Subscribes to ViewModel
  • ViewModel: Holds current state of the view, and notifies subscribers whenever it is updated.

Example: Consider the feed example defined in MVP

Model:

interface Repository{
fun getFeed(): Single<List<Post>>
}

ViewModel:

class FeedViewModel() : ViewModel() { 

val state = MutableLiveData<State>()

fun loadFeed(){
repository.getFeed()
.subscribe(feed -> state.postValue(State.SUCCESS(feed))
}
}

View:

class FeedFragment { override fun onViewCreated() {
viewModel.state.observe(state -> {
when(state){
is LOADING -> showLoader()
is SUCCESS -> showFeed(state.feed)
}
})
}
}

Problems that we solved

  • Single source of truth for our view state
  • Reactive updation of views
  • Decoupled MVP: In MVP, Presenters holds reference to Views & Views to Presenter. Changes to architecture weren’t that easy
  • Better unit testing: ViewModels don’t have reference to views unlike Presenters in MVP and can be easily unit tested.

The Advanced: Redux/MVI

In 2015, React Native was introduced to the mobile world. It was the first time, a good chunk of mobile developers were exposed to Javascript. Gradually, those coding in React Native started adopting famous React patterns & architectures. Most of them aimed at simplifying state management & data flow. The famous ones being Flux, Redux and MobX.

Gradually, patterns like these found their way to native development too. And MVI was born.

Basic Concept
If you google “MVI Android”, you’ll find multiple variations & libraries of the same, good enough to get you confused. For this discussion, I’ll restrict myself to basic Redux inspired from the original ReduxJs library.

The core motivation behind Redux was to simplify state management & make apps more predictable. To achieve this, there are three main principles behind Redux:

  1. Single Source of Truth (for your view or app state)
  2. State is immutable (i.e. you can’t directly modify it)
  3. State can only be updated by pure function

Essentially, redux has a state just like MVVM. However, you can’t directly modify that state. States in Redux are modified by a combination of actions & reducers. To modify a state you need to fire an “Action”. This action is consumed by something called “Reducer”. A reducer is simply a pure function that takes in current state & action and returns a new state. The new state, can now be used to update your UI.

Simply put, you fire an action to perform a use case which is consumed by reducer that returns a new state. Now, the next obvious question is if reducers are simple pure functions that just takes current state & action and returns a new state, where do we perform operations like network call, database query, etc? The answer to that question is “Side effect”. Redux has something called “side effect” where you perform all other operation that you want to do when an action is dispatched.

To summarise, Redux has following components:

  • State: Represents state of the application (or module or view)
  • Action: Fired to update state
  • Reducer: Pure function that computes new state
  • Store: Holds state
  • Side-effect: To perform operations other than reducing state
The Redux Flow

Example: Let’s consider the same example that we built earlier. A simple screen that displays feed.

State:

sealed class Stateclass LoadingState: Statedata class SuccessState(feed: List<Post>): Statedata class FailureState(errorMessage: String): State

Action:

sealed class Actionclass LoadFeed: Actiondata class LoadFeedSuccess(feed: List<Post>): Actiondata class LoadFeedFailure(errorMessage: String): Action

Reducer:

fun reduce(currentState: State, action: Action): State {
return when(action) {
is LoadFeed -> LoadingState
is LoadFeedSuccess -> SuccessState(action.feed)
is LoadFeedFailure -> FailureState(action.errorMessage)
}
}

Side Effects:

fun runSideEffects(currentState: State, action: Action, store: Store){
when(action){
is LoadFeed -> {
repository.loadFeed()
.subscribe(
feed -> store.dispatch(LoadFeedSuccess(feed),
error -> store.dispatch(LoadFeedFailure(error.message)
)
}
}
}

Advantages over MVVM?

  • MVVM is Cyclic is nature: View updates ViewModel, ViewModel updates View, which doesn’t sound that good. Isn’t it!!
  • As app grows, MVVM tends to become complex
  • Immutable source of data: In MVVM, you could change state (ViewModel) from anywhere. This could lead to unpredictable states & bugs. However, redux locks down on way you update your state (only via reducer)
  • App behaviour in Redux becomes much more predictable
  • It’s fun to unit test reducers :)

Libraries implementing MVI

PS: MVI is not exactly Redux. Also, there are slight differences between how MVI is implemented in all the above libraries. However, the core ideas & principles of them remain the same.

Show me the code

I’ve implemented a sample app using all three architectures — MVP, MVVM & MVI. This is pretty basic & doesn’t use any library. But it will give you an idea of how these architectures are different.

https://github.com/gauravsapiens/android-architecture-sample

--

--