20 July 2015
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:
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
@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:
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
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
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 company blog