Writing Testable Android MVVM App: Part 1. ViewModel

In the previous post, I set some goals and wrote a simple standard Android app to explore how we can convert it to a testable MVVM app. Let’s start with writing an MvvmApp library module with some basic key components:

  • ViewModel
  • ViewModelActivity
  • ViewModelFragment

The source code for this post is tagged as part1 and available on GitHub. This post will mainly focus on the big picture and not go into every detail. However, I made an effort to keep good, simple, iterative commits, to make it easy to go over the commits and see all the details. I encourage you to check out the source code on GitHub as well.

Quick Reminder
This is a part of a multi-post series where I’ll explore and iterate, so please remember that this isn’t meant to be a final solution.

MvvmApp Library

ViewModel
This will serve as the base class for all ViewModels. It extends from BaseObservable so that it can be used with Android Data Binding. We’d like the ViewModel to take care of itself when any life-cycle events happen, so we’ll add onStart(), onStop(), getInstanceState().

ViewModelActivity
This will serve as the base Activity for all Activities that use a ViewModel directly. You can see how the life-cycle events get passed to the ViewModel so that it can save/restore itself from configuration changes.

ViewModelFragment
Similar to ViewModelActivity, this will serve as the base Fragment for all Fragments that use a ViewModel directly.

Sample App

Let’s use the MvvmApp library from above to bring some MVVM into the sample app.

MainActivity
MainActivity is a pretty simple Activity which just has two buttons, each of them starting another Activity.

Main Activity

We’ll extend from ViewModelActivity, and use MainViewModel. As you can see below, this makes the Activity code pretty simple.

You’ll notice that I used ButterKnife to wire up the button clicks, instead of using android:onClick through Android Data Binding. This was done for two reasons:

  1. Using android:onClick via Android Data Binding requires the method signature to match. In other words, it requires View as a parameter (as explained well by @fabioCollini near the end of the post here).
  2. We need to pass in the Activity instance to the ViewModel, which you’ll see later (at least for this iteration).

I thought it might be lesser of the two evils to bind the onClick() via ButterKnife, as opposed to having custom Binding or introducing an unused parameter into the ViewModel. Feel free to make your own calls here.

MainViewModel
The MainViewModel will extend from ViewModel and will contain all the business logic that the MainActivity had in the standard Android app.

In the above code, it’s hard to tell whether there was an advantage in doing this. So let’s take a look at another example.

ClickCountActivity
In this Activity, we’ll have to update the text when the button is clicked, and also remember the click count value on configuration change.

Click Count Activity

The code for the Activity itself remains simple, just like MainActivity.

activity_click_count.xml
This time though, we’ll introduce ClickCountViewModel in the layout, and wire up clickCountText TextView to it, as explained on the Android Data Binding Guide.

ClickCountViewModel
And now, in the ClickCountViewModel, we’ll take care of updating the click count, notifying the View of the change, and restoring state. What’s happening here is that we’re just taking advantage of Automatic Setters (line 22 in the XML above) and the Bindable annotation. Since we have to handle configuration changes, we’ll remember our state in the ClickCountState.

One thing to note here is the usage of instanceof check when restoring state in the constructor. I didn’t want to use Parcel or Bundle directly, and also wanted to keep the ClickCountState class private. We’ll see how it turns out as we iterate.

RecyclerView
If there’s one thing to be said about RecyclerView, it’s easy to use directly but a bit tricky to use differently. It often led me to come up with non-trivial solutions or creative work-arounds, whether it was Robotium or Espresso. MVVM was no exception. So we’ll cover RecyclerView separately, as I believe it deserves its own section. We’ll also touch a bit of unit testing and see if our current MVVM solution is good enough.

Let’s move on to the next part, Writing Testable Android MVVM App: Part 2. RecyclerView!