Android Data Binding Part 1: Why it is important
Android application code often suffers from being more verbose than it could be. As libraries such as Android Annotations and ButterKnife have shown that's only partially due to tediousness of Java. The recently announced Android Data Binding library can remove at least part of the boilerplate code we need to write. Since I've always liked Presentation Model pattern (MVVM) this is very dear to my heart. However just getting rid of a tedious code is not the main reason I'm so happy to see the new API. Let's recap on common issues developer faces on Android and then I'll show how using mentioned patterns with new offering from Google can mitigate them.
Problems with classic approach
I'll explain that step by step using a registration form screen example. We need couple of fields like first name, last name and an email address. To make the example a bit more interesting and to make UX better we'll use floating label pattern. One of the simplest approaches without data binding (for first name only) might look like this:
Memory leaks
A seasoned Android developer will immediately spot that we've a potentially leaked activity here. The culprit is of course the anonymous inner class implementing RegisterApi
callback (Action2
), which in turn uses findViewById
. However it's not the mentioned method that retains the activity - in Java every non-static inner class will have implicit reference to the enclosing class instance. While this problem is so common I still haven't found a concise solution in the Android SDK. There are however community driven libraries i.e. Otto and RxJava that can help tackle this problem.
To much code in Activity
Many of us, me including, are guilt of stuffing too much into Activity
classes. Writing test and maintaining them can become a nightmare over time. The verbosity of Java only makes the matter worse. One way to slim activities down is to use Presenter pattern written either by hand or with the help of Mortar. Another solution is to encapsulate more logic into custom view classes used in layouts and using shared static helper methods with common code that calls Android API. Nevertheless, for a programmer that just starts with Android, it's important that the framework provides guidance and samples that encourage separation of concerns - in my opinion up until now that was rarely the case.
Maintaining layout files
Many if not most screens in Android (and other platforms, too) are read-only. That is they are not forms and fields that user can change. Instead they offer neatly presented content with some way of interacting with it (as opposed to changing it directly). When using Android layout files we're forced to use references (auto generated in R
class by aapt) that allow setting view's properties. This isn't something necessarily bad especially if you need to heavily interact with a View
. Having said that, how often you used findViewById
(or @InjectView
) just to set the content of TextView
or had to refactor activity fields type because of a change in a layout definition?
Android Data Binding can help you.
The following code illustrates how registration form might look like with data binding:
The new RegisterActivity
is similar to previous example. We inflate the layout differently to initialize data binding and get an instance of ActivityRegisterBinding
class. The class, with the name derived from layout name, is generated by data binding and its main purpose is to provide a glue between a view and an observable view model. When using registerApi.register
we only reference form
variable thus avoid unnecessarily referencing the activity. The RegisterForm
class is a view model for our layout and has all the data that activity_register
layout requires.
The view model has 3 observable attributes marked with @Bindable
annotation. We don't strictly need a backing field as the getFirstNameLabelVisibility
shows. What we do need is to fire change notification events, which is done via notifyPropertyChanged
and notifyChange
methods available on BaseObservable
. The former method accepts a field id that is assigned by data binding processor in generated class BR
(similarily to R
class). Deriving from BaseObservable
is not a must - to make a model bindable it has to implement android.databinding.Observable
.
Last but not least a layout file:
The data binding library, while still in beta, provides a nice toolset to remove the boilerplate code and improve Android code (unit) testability by extracting the logic from activity (or fragment). When used properly it can also save you from leaking resources. In the next post I'll explain in more detail how it happens.
This article is cross-posted with my personal blog