MVVM and DataBinding: Android Design Patterns

This article describes the MVVM Design Pattern and its components, data binding, and other design patterns and architectural concepts for the Android platform. By Matei Suica.

4.5 (47) · 1 Review

Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Testing Each Component

Now take a moment to review how you test each of the components covered so far.

  • View: This is the simplest component in MVVM. It doesn’t know about any other component and it doesn’t control anything. Testing the View is also simple. First, provide some controlled data just like a ViewModel would. Then, check the visual results. Voilà!
  • Model: This is usually simple to test also. The Model has no knowledge about the external world so you have complete control over it. All you have to do is to call “execute” methods on a Model object and then check the results with query method calls.
  • ViewModel: Testing this component is a little tricky. You have to mock the Model and call methods on the ViewModel. After that, check that the data available for the View is correct. You can also check that the methods called on the mock Model are correct. If the ViewModel depends on other components, you need to mock those as well. Still, testing the ViewModel is easier than testing the components of any of the previously mentioned design patterns.

Best practices in Testing MVVM

Testing is straightforward when you apply a good pattern. There are a few secrets to testing MVVM, but here’s the best approach:

  • Start testing the ViewModel. Test each one of the public methods, going through as many cases as you can. Not just the obvious paths, but real edge cases that you’re sure are never going to happen. Trust me: users and future developers will reach those edge cases. :]
  • If something isn’t testable, wrap it! Create an interface that contains all of the methods you need from the untestable class. Create a class that implements that interface and instantiate the untestable class you wanted to use inside of it. Then, inside your class, call the methods from the untestable class. This is called the Proxy design pattern.

Now you have an interface you can use in your app that’s easily replaceable with a mock object. This way, you can “throw” untestable dependencies outside your class.

You should keep Android SDK framework components and external libraries at arms-length. By wrapping them this way, you make sure you don’t spread dependencies all over the code. You use the interface you’ve created and keep the frameworks and libraries in a single place.

Keep your tests short and make sure they are repeatable.

It’s not your responsibility to test the framework. You can’t control the framework, so there’s no point in testing it. If your Model only forwards methods to another library like Room, there’s not much to do in terms of testing. You can add tests once your Model needs to do something else, like filtering or computing.

Test your View with different device configurations. Mock the ViewModel to generate all the states that a View could receive.

Adding Android’s Architecture Components

So far, you’ve seen the main MVVM components and discussed testing them. But what about the Binder layer? The Android SDK provides Data Binding to implement a Binder.

Using DataBinding

Data binding is a technique that connects data sources with consumers and keeps them in sync. Android’s Data Binding Library lets the developer keep the UI in the layout XML files and generates code from them. The library also does the heavy lifting of synchronizing the View with data that comes from the ViewModel.

To add data binding to your project, you need to add the following to your app’s build.gradle file:

android {
  ...
  dataBinding {
    enabled = true
  }
}

Now, your layout XMLs will need to have the following structure:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="user"
            type="com.example.User" />
    </data>
    <ConstraintLayout... /> <!-- UI layout's root element -->
</layout>

To transform XML from the default structure to a data binding one, you need to wrap the root layout, in this case a ConstraintLayout, inside a layout tag. Then, at the top of the layout, just inside the layout tag, you will need to add a data tag, containing the objects that are used for binding.

Note: Once you have data binding enabled and sync your project, Android Studio offers a shortcut for transforming any layout into a data binding one. Select the root element of your layout and press option + return. A pop-up appears with the option “Convert to data binding layout”. Select that and the layout will add the data and layout tags automatically.

Note: Once you have data binding enabled and sync your project, Android Studio offers a shortcut for transforming any layout into a data binding one. Select the root element of your layout and press option + return. A pop-up appears with the option “Convert to data binding layout”. Select that and the layout will add the data and layout tags automatically.

The UI will reflect every change in the ViewModel via the variables inside the data tag. In the example, there is a variable named user.

You have to tell the UI elements what property they need. For example:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <LinearLayout
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.firstName}"
   android:id="@+id/firstName"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"
  android:id="@+id/lastName"/>
   </LinearLayout>
</layout>
</xml>

Here there are two text views that are bound to the user.firstName and user.lastName values.

You will still need to add different listeners to your UI and forward those events to the ViewModel. Lucky for you, the DataBinding library can also help with that. The library generates a binding object for accessing the UI elements:

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  val binding: MyLayoutBinding = MyLayoutBinding.inflate(layoutInflater)
}

In the above, you create a binding value using the generated binding object.

Adding Android’s own ViewModel

Creating your own ViewModel class is great, but Android Jetpack offers more. Jetpack’s ViewModel has its own lifecycle, managed by the library. This means that the library will keep the ViewModel when the OS recreates the Activity. You also don’t need to synchronize the UI state.

To begin, create a class that inherits from Jetpack’s ViewModel:

class ProfileViewModel : ViewModel() {
  val uiModel: MutableLiveData<UiModel> by lazy {
    MutableLiveData< UiModel >()
  }
}

Then, in your Activity or Fragment, add this:

val model = ViewModelProviders.of(this).get(ProfileViewModel::class.java)

If the activity is re-created, it receives the same ProfileViewModel instance as before. When the owner activity finishes, the framework calls the ViewModel object’s onCleared() method. That’s when the ViewModel is really destroyed.