What is MVVM?

Model-View-ViewModel is an architecural approach used to abstract the state and behaviour of a view, which allows us to separate the development of the UI from the business logic. This is accomplished by the introduction of a ViewModel, whos responsibility is to expose the data objects of a model and handle any of the applications logic involved in the display of a view.

This approach (MVVM) is made up of three core components, each with it’s own distinct and separate role:

Model - Data model containing business and validation logic

- Data model containing business and validation logic View - Defines the structure, layout and appearance of a view on screen

- Defines the structure, layout and appearance of a view on screen ViewModel - Acts a link between the View and Model, dealing with any view logic

So how does this differ from the MVC approach that we’re used to? The architecture for MVC is as follows:

The View sits at the top of the architure with the Controller below it, followed by the Model

sits at the top of the architure with the below it, followed by the The Controller is aware of both the View and Model

is aware of both the and The View is aware of just the Model and is notified whenever there are changes to it

In MVVM the architecture is similar, but there are a few distinct differences:

The Controller is replaced by a View Model , which sits below the UI layer

is replaced by a , which sits below the UI layer This View Model exposes the data and command objects that the View requires

exposes the data and command objects that the requires The View Model receives its data from the Model

You can see here that the two approaches use a similar architecture, with the addition of a View Model and the way that it introduces a different approach to the communication between components. The architecture introduces two-way communication between its components, whereas MVC is only capable of one-way communication.

In a nutshell, MVVM is a progression of the MVC architecture - using an additonal layer of non-visual components on top of the Model (but below the View) to map data closer to the View components in the architecture. We’ll take more of a look at the nature of MVVM over the next few sections.

The Hacker News reader

As previously mentioned, I took an old project of mine and stripped it back for use with this article. The features of this sample application consist of:

Retrieval of Posts

Viewing a single Post

Viewing comments for a Post

Viewing a selected authors Posts

This was done in the hope that it would reduce the codebase, hence making it a little easier to follow and understand how the implementation operates. The screens of the app that we’re working with are as shown below:

MVVM has been used to implement the two screens shown here

The main part of the application that I’m going to be looking at is the listing of Posts, shown on the left. The comments screen works in pretty much the same way, with a few slight differences (which we’ll look at later).

Displaying Posts

A Post is an item within a RecyclerView, displayed within a card.

Each Post instance is displayed in a recycler view within a card view, as shown on the left.

Using MVVM we will be able to abstract the different layers that make up this card, meaning that each MVVM component will only be dealing with its assigned responsibility. Using these different components introduced with MVVM, working together they are able to construct the Post card instance. So how can we break this up?

How some parts of the Post card are built in the sample app, using MVVM

Model

Quite simply put, the Model consists of the business logic belonging to a Post. This includes different properties such as the id, name, text etc. The code below shows a reduced version of this class:

The Post Model, stripped back of Parcelable and other methods for readability

Here you can see that all our Post Model contains is it’s properties, no other logic has been placed in this class - that’ll be dealt with by the other components.

View

Our View is responsible for defining the layout, appearance and structure of its components. The View itself will be (ideally) constructed completely of XML, however if any java code is used then it should not consist of any business logic. The View retrieves its data from a View Model through the use of binding. Then at run time, the UI content is set and can be updated when the View Model properties flag any change notification events.

To begin with, we created a custom adapter to use with our RecyclerView. For this, we needed to make a create a BindingHolder to keep a reference to our Binding.

public static class BindingHolder extends RecyclerView.ViewHolder {

private ItemPostBinding binding;



public BindingHolder(ItemPostBinding binding) {

super(binding.cardView);

this.binding = binding;

}

}

The onBindViewHolder() method is where the actual binding of the ViewModel and View takes place. We create a new ItemPostBinding (generated from our item_post layout) and set the View Model to a new instance of our PostViewModel class.

ItemPostBinding postBinding = holder.binding;

postBinding.setViewModel(new PostViewModel(mContext,

mPosts.get(position), mIsUserPosts));

Other than the standard adapter views, that’s pretty much it! The full PostAdapter class is displayed below:

Moving on to our XML layout file, we first begin by wrapping our entire layout in a <layout> tag and declare our ViewModel using the <data> tag:

<layout xmlns:android="http://schemas.android.com/apk/res/android"> <data>

<variable name="viewModel" type="com.hitherejoe.mvvm_hackernews.viewModel.PostViewModel" /></data> <!-- Other layout views --> </layout>

Declaring our View Model is required to allow us to reference it throughout our layout file. I’ve made use of the ViewModel in several places within the item_post layout:

androidText - It’s possible to set the content of a text view by referencing the corresponding method in our ViewModel. You can see below the use of @{viewModel.postTitle} , this references the getPostTitle() method in our ViewModel - which returns us the title of the corresponding post instance.

- It’s possible to set the content of a text view by referencing the corresponding method in our ViewModel. You can see below the use of , this references the method in our ViewModel - which returns us the title of the corresponding post instance. onClick - We can also reference click events from our layout file. As shown in the layout file, @{viewModel.onClickPost} is used to reference the onClickPost() method in our ViewModel, which returns an OnClickListener containing the click event.

- We can also reference click events from our layout file. As shown in the layout file, is used to reference the method in our ViewModel, which returns an OnClickListener containing the click event. visibility - The ability to open the comments activity for a post depends on whether the post has any comments or not. This is done by checking the size of the comments list and setting the visibility based on the result, which should take place in the ViewModel. Here, we use the getCommentsVisiblity() method which returns the calculated visibility.

That’s great isn’t it? This allows us to abstract the display logic from our layout file, handing it over to our ViewModel to take care of it for us.

ViewModel

The ViewModel is the component which acts as the link between the View and the Model, giving it the responsibility of all of the logic relating to our View. The ViewModel is responsible for accessing the methods and properties of the Model, which is then made available to the View. Within our ViewModel, this data can be returned as is or formatted to again remove this responsibility from other components.

In our case, the PostViewModel uses the Post object to handle the display of content on the CardView of a Post instance. Within this class (below) you can see a whole bunch of methods, each corresponding to a different property of our Post View.

getPostTitle() - This uses the Post instance to return the Post title

- This uses the Post instance to return the Post title getPostAuthor() - This method begins by retreiving a String from the app resources and formatting it with the author of the Post instance. Then if our isUserPosts equates to true we underline the text, finally returning our content String

- This method begins by retreiving a String from the app resources and formatting it with the author of the Post instance. Then if our equates to true we underline the text, finally returning our content String getCommentsVisibility() - This method returns the value that should be used for the comment TextViews visibility

- This method returns the value that should be used for the comment TextViews visibility onClickPost() - This method returns a click event when the corresponding view is pressed

These samples show the different kinds of logic that can currently be handled by our ViewModel. Below shows the complete PostViewModel class and its methods which are referenced from our item_post view.

Great, huh? As you can see, our PostViewModel takes care of:

Providing the Post objects properties to be displayed in our view

Carrying out any required formatting on these properties

Returning click events to any views using the onClick attribute

Handling the visibility of any views based on Post properties

Testing the ViewModel

One of the great things about MVVM is that our View Model is now extremely easy to unit test. For the PostViewModel, a simple test class was created to test that the methods in the ViewModel were implemented correctly.

shouldGetPostScore() - Test the getPostScore() method, to ensure that the score for the Post is correctly formatted as a String and returned.

- Test the getPostScore() method, to ensure that the score for the Post is correctly formatted as a String and returned. shouldGetPostTitle() - Test the getPostTitle() method, to ensure that the correct Post title is returned.

- Test the getPostTitle() method, to ensure that the correct Post title is returned. shouldGetPostAuthor() - Test the getPostAuthor() method, to ensure that a correctly formatted string using the Post author is returned.

- Test the getPostAuthor() method, to ensure that a correctly formatted string using the Post author is returned. shouldGetCommentsVisiblity() - Test that the getCommentsVisibility() method returns the correct visibility for the ‘Comments’ button on the Post card. We pass ArrayLists of different states to ensure that the correct visibility is returned for each case.

And now we know that our ViewModel is working as it should, great!

Comments

The approach used for comments is very similar to that of the Post instances, however there is one difference that I would like to point out.

Two different view models are used regarding the comments, the CommentHeaderViewModel and CommentViewModel. If you look at the CommentAdapter then you’ll notice two different view types, which are:

private static final int VIEW_TYPE_COMMENT = 0;

private static final int VIEW_TYPE_HEADER = 1;

If the Post type is an Ask post, then we show a header section at the top of the screen containing the question which was asked - the comments are displayed as normal below. You’ll notice in the onCreateViewHolder() method we inflate the layout based on the VIEW_TYPE that we are currently dealing with, this simply returns one of our two different layouts.

if (viewType == VIEW_TYPE_HEADER) {

ItemCommentsHeaderBinding commentsHeaderBinding =

DataBindingUtil.inflate(

LayoutInflater.from(parent.getContext()),

R.layout.item_comments_header,

parent,

false);

return new BindingHolder(commentsHeaderBinding);

} else {

ItemCommentBinding commentBinding =

DataBindingUtil.inflate(

LayoutInflater.from(parent.getContext()),

R.layout.item_comment,

parent,

false);

return new BindingHolder(commentBinding);

}

Then in our onBindViewHolder() method we create the binding depending on the type view that we’re dealing with. This is because we’re using a slightly different View Model for the cases when there is a header section (for our ASK post question text) used.

if (getItemViewType(position) == VIEW_TYPE_HEADER) {

ItemCommentsHeaderBinding commentsHeaderBinding =

(ItemCommentsHeaderBinding) holder.binding;

commentsHeaderBinding.setViewModel(new

CommentHeaderViewModel(mContext, mPost));

} else {

int actualPosition = (postHasText()) ? position - 1 : position;

ItemCommentBinding commentsBinding =

(ItemCommentBinding) holder.binding;

mComments.get(actualPosition).isTopLevelComment =

actualPosition == 0;

commentsBinding.setViewModel(new CommentViewModel(

mContext, mComments.get(actualPosition)));

}

And that’s pretty much all that is different about it, the comments section just has two different ViewModel types available - the one chosen is dependent on whether the post is an ASK post or not.

To conclude…

The data binding library, if used correctly, has the potential to really change the way in which we develop applications. There are other ways in which we could make use of data binding in our applications, using an MVVM structure is just one of the ways in which we can do so.

For example, we could simply reference our Model in the layout file and access its properties through a variable reference:

<data>

<variable name="post" type="your.package.name.model.Post"/>

</data> <TextView

...

android:text="@{post.title}"/>

This is simple to do and could help to remove some basic display logic from adapters and/or classes. Whilst this is nice, a similar approach could result in the following:

<data>

<import type="android.view.View"/>

</data> <TextView

...

android:visibility="@{post.hasComments ? View.Visible :

View.Gone}"/>

Me upon seeing the above.

For me, this is where Data Binding could have a negative effect on its usage. This is moving a Views display logic into the View itself. Not only do I find this messy, but it would also make testing / debugging more difficult by mixing logic and layout code together.