In this post, I’ll cover how to make an Android To Do application that keeps track of your tasks. Let’s begin with first addressing Android Data binding, which is a powerful technique that can help you remove boilerplate code, letting the code you write be terser. In turn, this can allow you to focus on the business logic of your application. Google’s data binding library is a support library and can be used all the way back to API 7.

What’s Data binding?

Data binding is a software design pattern that allows UI elements to be bound to underlying models. For Android, this could mean tying the text property of a TextView to a String value in a Plain ‘Ol Java Object (POJO). In practice, this means you can eliminate lots of tedious code by indicating in XML, the mapping to your objects.

Architecture

If you aren’t using data binding, I am going to assume that you are using a pattern similar to Model View Presenter (MVP). In MVP, you use the Presenter as your “man in the middle” between your view and your model. When using data binding, you could directly map view and model logic, but this should be avoided. The reasons a developer would introduce a layer between the view and the model still exists despite the capability of binding data more closely to the UI. When using data binding, we call that layer the View-Model. The pattern we will be exploring while using data binding is known as Model View View-Model (MVVM). The View-Model serves the same role as a Presenter normally would, with the caveat that it uses data binding.

Related: MVVM on Android Using the Data Binding Library

Let’s Pair!

For this article, we are going to explore making a simple To Do application that keeps track of your tasks. We are going make two screens that achieve the same task, one using MVP and one using MVVM which will hopefully illustrate some of the pros and cons of these two patterns. The app will be separated into two sections: a list of tasks and an add-task screen.

Getting Started

For the initial setup, we are going to use dependency injection to help separate object creation and dependency management from our Fragments. While this has nothing to do with MVVM or MVP, it’s a good practice to get into and is one of the first steps my colleagues and I take when creating an Android application. The most popular Android library for dependency injection is Dagger. Now, let’s modify your app/build.gradle

...
android {
  ...
  dataBinding {
    enabled: true
  }
}
...
dependencies {
  ...
  compile 'com.google.dagger:dagger:2.1'
  annotationProcessor 'com.google.dagger:dagger-compiler:2.1'
}    

Congratulations, you’ve done everything you need to have a lean, mean, dependency-injected, data-bound machine.

Dagger

We are going to speed through the setup of our application’s modules and components with Dagger. For more information, please check out the repository. At a high level, we are going to extend the Application class and add a component for use with it called DatabindingExampleApplicationComponent which has three modules MainModule, MvpModule, and MvvmModule. The MainModule will be responsible for providing core application dependencies, while the MvpModule and MvvmModule will provide their respective Presenters and View-Models. This is a nice, clean separation of concerns that will suit our purposes.

Comparative Development

Making a ListView

Our To Do application needs a way to view various To Do items on your schedule. Let’s explore this through the lens of these two similar-but-different architectures to see if we can determine where advantages and disadvantages lie in our approaches. We will make two naive implementations of a List of To Do items. For the sake of clarity and brevity, I am not implementing the fully functional To Do list of your dreams. I’ll leave that as an exercise to the reader.

Let’s design our list view fragment’s UI, now. Our MainActivity will serve as our application’s launch pad and host two separate buttons for launching into either a Fragment which uses MVP or a Fragment which uses MVVM. Functionally, these fragments will do the same thing but in two different ways which will hopefully articulate the differences between approach better than I can with mere human being words.

Our ListFragment from the MVVM package has a ListViewModel which is injected and provides the adapter for the RecyclerView within it. Similarly, our ListFragment from the MVP package uses a ListPresenter which provides the adapter for its RecyclerView. Each RecyclerView is responsible for displaying individual Todo model items. Their layouts are structurally the same aside from initial markup, providing information about what kind of View-Model it has. In exchange for additional markup specifying how the View-Model supplies information, you lose markup for identifying the view. In this way, the underlying Fragment stops caring about the details of any particular XML file describing the view hierarchy. I count this as a win for data binding.

When you look at the RecyclerView.Adapter for the mvp implementation, we can see particular pain points where coupling is occurring:

@Override public void onBindViewHolder(TodoViewHolder holder, int position) { Todo todo = todoList.get(position); holder.titleTextView.setText(todo.getTitle()); holder.dateTextView.setText(simpleDateFormat.format(todo.getDate())); holder.descriptionTextView.setText(todo.getDescription()); } 

This illustrates how coupling is more prone to occur in these circumstances. We are formatting and altering the UI from within an adapter. 

public class TodoViewHolder extends RecyclerView.ViewHolder {
    private final TextView titleTextView;
    private final TextView descriptionTextView;
    private final TextView dateTextView;

    public TodoViewHolder(View itemView) {
        super(itemView);
        titleTextView = (TextView) itemView.findViewById(R.id.title);
        descriptionTextView = (TextView) itemView.findViewById(R.id.description);
        dateTextView = (TextView) itemView.findViewById(R.id.date);
    }
}

We have effectively tethered the view to the code with this implementation. Alterations to view structure necessitate changes here. This can be avoided with data binding.

When approached through the lens of MVVM, we can see how we have decoupled the specifics of the view and the presentation of the data. Let’s examine the RecyclerView.Adapter from the mvvm package:

public class TodoViewHolder extends RecyclerView.ViewHolder {
        private final ItemTaskBinding binding;

        public TodoViewHolder(ItemTaskBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }

        public void bind(Todo todo) {
            binding.setViewModel(new TodoViewModel(todo));
        }
    }

In the MVVM implementation of this, we can see that we have removed all references to specific views in our Todo list items which means we can alter what data goes where and how it’s displayed without the fear of needing to alter anything in this entire class.

The advantages gained from data binding might seem modest at this point in the application. When you are tasked with more than simple, read-only output of data, the advantages become more apparent.

Making the add view

Our app needs some way to add Todo items. Both implementations have three text fields and a button. Both present a dialogue to the user when the date field is selected, and both update the date field and prepopulate it with a formatted date. However, the differences between the MVP fragment and presenter and the MVVM fragment and View-Model implementations are striking.
The separation of concerns has broken down using MVP in a core way. The fragment has become responsible for knowing when the view is updated and jamming that information to the Presenter at the appropriate time using the sequence of listeners.

Let’s take a look at some of the differences as illustrated by setting the title on the model using both implementations:

<android.support.design.widget.TextInputEditText
 
android:id="@+id/what_text_input"
 android:layout_width="match_parent"
 
android:layout_height="wrap_content"
 
android:hint="@string/what_to_do"/>
// Fragment onCreate
...
whatTextField = (EditText) view.findViewById(R.id.what_text_input);
whatTextField.addTextChangedListener(new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

    }

    @Override
    public void afterTextChanged(Editable s) {
        addPresenter.setWhatToDo(s.toString());
    }
});
// Presenter
private String whatToDo;
...
void setWhatToDo(String whatToDo) {
    this.whatToDo = whatToDo;
}

Even if you were to move the anonymous inner class into a property of the body, you still have to inform the input of the Presenter’s existence.

The View-Model is a better abstraction for its coupling. It leaves fewer places to make mistakes and more importantly, it’s easier to understand by forcing you through fewer hoops:

// MVVM layout

<android.support.design.widget.TextInputEditText
 
android:text="@={viewModel.whatToDo}"
 
android:layout_width="match_parent"
 
android:layout_height="wrap_content"
 
android:hint="@string/what_to_do"/>
// View-model
@Bindable
public String whatToDo = "";   

This helps you out a lot if features come in that alter the view or behavior. We can see that we are really just binding a model’s data to the view. A Presenter plays a more dubious role, however, because it has to get wrapped up in the specifics of how a view is structured. Because of change, say a label becoming editable or a field being added to the model, the developer is forced to dive between classes more often, inevitably leading to more mistakes.

You also have to remember to communicate backward appropriately, as exemplified by the interface dance between the fragment and Presenter when the user is presented with a DatePickerDialog:

//inside Presenter
private DatePicker.OnDateChangedListener onDateChangedListener;
...
void setOnDateChangedListener(DatePicker.OnDateChangedListener onDateChangedListener) {
    this.onDateChangedListener = onDateChangedListener;
}
...
@Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
    ...
    onDateChangedListener.onDateChanged(view, year, month, dayOfMonth);
}
//inside Fragment onCreate
whenTextField = (EditText) view.findViewById(R.id.when_text_input);
//An example of an easy mistake: I missed putting this in on my first pass at this example 
whenTextField.setOnClickListener(addPresenter::onDateClicked); 
whenTextField.setText(addPresenter.getDateString()); 
addPresenter.setOnDateChangedListener((view1, year, monthOfYear, dayOfMonth) -> whatTextField.setText(addPresenter.getDateString())); 
// I also accidentally set this to the whatTextField instead of the whenTextField in my first pass

The simplification in logic for MVVM boils down to binding directly on one method (which is common to both implementations, minus the annotation):

@Bindable
public String getDateString() {
    return Formatters.TODO_DATE_FORMATTER.format(getDate());
}

Losing all of that intercommunication can save you time and headaches. In the first pass of writing this example repository, I screwed up two important pieces which produced bugs. MVVM can keep you from making mistakes by saving you hassle and keeping a clean separation between your fragment, Presenter/View-Model, and view. I hope you have enjoyed this post; you can learn more by cloning the repository.

 git clone git@github.com:erikist/DatabindingExample.git 
Erik Rahtjen

Software Engineer at Stable Kernel

Leave a Reply

Your email address will not be published. Required fields are marked *