CC0 by Luis Llerena at StockSnap.io

Refactoring to MVVM

A way to learn MVVM with real code

If you read my previous article “Refactoring to MVP” and asked yourself — What about MVVM? — Here’s the counterpart.

MVVM stands for Model-View-ViewModel and it’s another software development pattern intended to separate UI code from business logic code, and specifically on Android, to separate Android code from the rest.

The Model takes care of providing the data and communicating with our services, should be easy to replace, testable and independent of the user interface.

The ViewModel acts as a man-in-the-middle, translating the data from the Model to something the View can use, and passing the requests from the View to the Model.

The View implements the user interface and holds the ViewModel.

The Presenter becomes the ViewModel

Compared to MVP, the Presenter becomes the ViewModel, drops the reference to the View and uses Observables to provide asynchronous answers to the View.

There are two main ways of implementing MVVM in Android:

  • with RxJava Observables
  • with Android Data Binding

I will focus on the first option, as I am not a fan of Android Data Binding.

I’ll assume that you are new to RxJava, so I will try to explain carefully how Observables are used in this guide.


Initial Code

The following Activity contains a call to the Google Books API and displays the result into a ListView with an Adapter. (You can just scroll over it)

This is a typical MainActivity that implements everything inside.

The MainActivity knows too much

We can notice the following issues:

  • The MainActivity knows too much about the implementation. i.e. we use Retrofit and a callback to get the data
  • This breaks the single responsibility principle: the MainActivity is taking care of retrieving data and displaying it
  • Testing different Book results would mean having to modify the MainActivity, comment out code, etc. It would be easier if we could just replace one object

Your goal when implementing MVVM for this Activity:

  • Switch from Retrofit Callable to RxJava Observable with Retrofit’s RxJava extension.
  • Move Retrofit code into the Model, so the MainActivity knows less about the implementation and the responsibility of connecting to the API passes to the Model
  • Subscribe the View to the ViewModel’s results

Switching to RxJava

Before you can start moving things outside the MainActivity, you need to switch the Retrofit solution to RxJava. This step requires you to add the RxJava library dependencies to your project.

Not everyone likes RxJava (but I do)

Be aware that RxJava is considered by some “a monster” because it is a big library (more than 5K methods) and your Project Lead may not be happy about it. On the contrary, MVP doesn’t require you to add more libraries to your project.

Add the following dependencies to your project build.gradle:

//In app/build.gradle
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'io.reactivex:rxjava:1.2.1'
  • adapter-rxjava is a library that allows you to use RxJava with Retrofit
  • rxandroid is a library that adds the necessary Android specific requirements for RxJava

Now it’s time to change the code. In the Retrofit’s service Interface GoogleBooksService change Call to Observable:

Call<BookSearchResult> search(@Query("q") String search);
// change it to
Observable<BookSearchResult> search(@Query("q") String search);

Check the imports: we want to use rx.Observable, not java.util.Observable, so be aware of what Android Studio imports!

Now we need to change the MainActivity to use the Observable.

This old code

service.search("search+" + formatUserInput)
.enqueue(new Callback<BookSearchResult>() {

@Override
public void onResponse(Call<BookSearchResult> call,
Response<BookSearchResult> books) {
updateUi(books.body().getBooks());
}

@Override
public void onFailure(Call<BookSearchResult> call,
Throwable t) {
t.printStackTrace();
}
});

changes to

subscription = service.search("search+" + formatUserInput)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BookSearchResult>() {
@Override
public void onCompleted() {

}

@Override
public void onError(Throwable e) {
e.printStackTrace();
}

@Override
public void onNext(BookSearchResult bookSearchResult) {
updateUi(bookSearchResult.getBooks());
}
});
Subscribe to events, unsubscribe when done

The code looks similar to the Callback one, these are the differences:

  • search(String) returns an Observable that will emit a BookSearchResult
  • you need to “subscribe” to that Observable, so you will receive a “next” event containing the BookSearchResult
  • in case there’s an error, you will receive an “error” event
  • subscribeOn specifies to RxJava that we want to use the IO thread to perform the network call (and NOT the Main Thread)
  • observeOn specifies to RxJava that we want to process the events we receive in Android’s Main Thread (the UI)
  • keep the “subscription” somewhere, because you will want to unsubscribe when leaving the Activity

This is the Observable pattern in a nutshell: Subscribe to events, unsubscribe when you are done.


Creating the Model

Our Model will be a Java Interface that will provide a method to access to the Google Books API search call.

First create an Interface that will provide us data. Then create an implementation of that Interface. Move all the Retrofit code into it.

The Interface is the important part here. It only contains the definition of the methods available in the Model and should be kept as simple as possible.

Important notes:

  • the Retrofit configuration code has been moved into the constructor
  • the search method returns an Observable of BookSearchResults
  • I moved the “search+” part into the Interactor implementation
  • the Observable already has the subscribeOn on it
  • specify the Call Adapter Factory to be able to use RxJava

Creating the ViewModel

Now this is the biggest difference with the MVP example, our ViewModel will be very simple and will do just one thing: Make sure that we are using the right thread.

If you are formatting data, this is the place to do it, you can use RxJava map methods to transform one object to another, and there’s a huge list of RxJava operators that you can use for all imaginable actions.

The BooksViewModel code:

This ViewModel needs two parameters: The Interactor and the Main Thread scheduler. Because AndroidSchedulers is an Android dependency, I decided to move it out and pass it as a parameter.

The ViewModel is just calling to the Interactor search method, making sure that the events are observed in the correct thread.


Updating the View

The Model and the ViewModel are ready. Now it’s time to update the View.

Important changes:

  • use a CompositeSubscription to keep all the Subscriptions at hand, and call to unsubscribe() in the onDestroy
  • the subscribeOn and observeOn methods are now part of the Model and the ViewModel
  • the ViewModel is initialized in the onCreate (just like the Presenter in MVP)
  • pass the AndroidSchedulers.mainThread() to the ViewModel as a dependency

So.. where’s the improvement?

There’s not a lot of business logic in the original MainActivity, so it’s hard to quickly see why this code is better.

One of the biggest improvements is testing. If we want to provide our own list of books to test our UI, we just need to create a mocked BooksInteractor:

With Observable.just() we can create an Observable that will emit our own BookSearchResult with your own list of books.

So in the MainActivity we change it to use the mock:

booksViewModel = new BooksViewModel(new BooksInteractorMock(), AndroidSchedulers.mainThread());
No View Interface means less classes than MVP

The biggest improvement against MVP is that we don’t need a View Interface anymore. The ViewModel doesn’t know anything about the View. This makes the codebase smaller than with MVP and scales better when you have multiple View and ViewModels, frequently ones inside the others, and as well, you don’t need to check if the View is null or not inside the Presenter.

Secondly but not less important, you can have multiple ViewModel per View. Sometimes the problem with MVP it hat the Presenter becomes a God object that has too many responsibilities, with MVVM you can have multiple ViewModel, each one implementing a part of your UX.

The MainActivity handles subscriptions and errors

The biggest disadvantage against MVP, besides requiring RxJava, is that the MainActivity still holds some responsibilities: Both handling the subscription and errors.


In my opinion, choosing between MVP or MVVM boils down to these different factors:

  • Refactoring to MVP is easier with legacy code
  • MVP does not require RxJava
  • MVVM scales better on more complex projects
  • You can have multiple ViewModel per View, while in MVP the relationship is one to one

All code from this project is available here: https://github.com/miquelbeltran/android_book_listing/tree/refactor/mvvm in the refactor/mvvm branch. The MVP code is available in the refactor/mvp branch.


Thanks for getting this far! I hope you enjoyed this MVVM tutorial. I would love to know what do you think, and which pattern are you going to use on your next project. Also it would be awesome if you click the little ❤️ and share the article so more people would benefit from it.

If you are interested in more Android development, please check my other articles, follow me on Twitter or Check my GitHub projects.