Article

You’re Misusing MVVM

Collin Flynn

November 20, 2018

Psst: Time flies, and so does Android Architecture discussion.The developer relations team at Google has been hard at work providing new docs, coding samples, and guidance for developers of all skill levels. I’ve preserved the following article in its 2018 form, but check out how far things have come over at the architecture guide . The principles of unidirectional data flow (described below) are commonplace in the recommended architecture, jetpack compose, reactive programming models. Thanks Google!

But who can blame you when the Google Samples do too?

As the Android ecosystem has grown, Google finally got around to having an opinion on application architecture and provided some useful components for the everyday developer. These components (like LiveData and ViewModel ) often get categorized under Model-View-ViewModel: a broad architecture pattern that addresses some problems with another community favorite pattern, Model-View-Presenter. However, importing LiveData and ViewModel into your project doesn’t mean you’ll actualize their benefits — you have to follow through with the implementation they enable.

In this article, we’ll take a close look at a common mistake developers make which undermines a major advantage of MVVM.

First, a quick look at MVP and its problems.

MVP Benefits and Drawbacks

When the Android developer community initially rallied around MVP, the benefits were clear:

  • Business logic is in pure Java which is testable on the JVM. No more slow emulator tests.
  • Activities, Fragments, and Views get decluttered.
  • Business logic can be injected instead of embedded.

That’s a big upgrade from no architecture at all.

Still, it has drawbacks:

  • Views and Presenters typically hold a reference to each other — they’re coupled through a method contract.
  • Each instance of Presenter and View know about their partner and it’s hard to develop one in isolation from the other.
  • A small portion of business logic necessarily persists in the view layer. Its responsibility is to understand and correctly call numerous presenter methods.

Wouldn’t it be nice to implement the entire UI layer without caring at all about the presenter below? Teams like to divide and conquer work, and MVP makes that harder.

Wouldn’t it be nice to switch UI components without touching a shared interface or surgically altering how the Activity/Fragment/Dialog interacts with its presenter? And wouldn’t it be nice to test a Presenter and a View without mocking any methods from its partner?

The Android architecture components ViewModel and LiveData help achieve decoupling, but they don’t enforce it. You have to implement decoupled VMs. Disappointingly, the Google samples don’t demonstrate how. Keep reading.

Stop Directly Calling ViewModel Methods

A View layer component directly calling a method on a ViewModel. Don’t.

This is an old habit from MVP, and if you do this you’ll subvert the architecture and reintroduce MVP’s drawbacks.

Instead, your View layer emits events and displays state. That’s all it does.

The View layer component broadcasts “actions”: user input or events directly related to UI. It also draws the screen with “state”, the raw pre-formatted information that can be painted directly onto screen without having to make decisions.

Handle actions in your ViewModel and emit new state

A ViewModel observes actions from an unknown source. It emits new states to an unknown observer.

Test by pushing in states and observing actions

A View component receives new states from a test emitter, and a test observer validates any actions emitted from the View
A test emitter pushes actions into a ViewModel and a test observer watches what states the ViewModel emits.


Don’t miss out on the advantages of MVVM:

  • Keep Views dumb: they broadcast facts about themselves like clicks and user input. They don’t decide what methods to call on anyone else.
  • ViewModels have no idea where the actions come from, nor who’s observing state. A ViewModel could be reused to handle a foreground or background file download, for example.
  • There’s no cumbersome method contract.
  • Views can’t fail to call a ViewModel method because they don’t call ViewModel methods. Views don’t make decisions, and what they do can be easily tested by observing their action emissions.
Collin builds decoupled stuff with Livefront , and he thanks you for reading.