Android Data Binding is a support library that allows us to bind UI components to data sources declaratively rather than programmatically, it’s potentially really powerful and complex, but used effectively it can really cut down on presentation boilerplate.

We started off with Data Binding beta in 2015 when Google announced it after Google IO as a support library that can be used right back down to Android 2.1 which is API version 7, to write declarative layouts and minimise the glue code required to use your application logic in layouts.

With the Android Gradle Plugin 3.1, it includes the new Data Binding Compiler v2, this includes incremental class generation, so this means that it will speed up your build times a little bit, and it will include precompilation of classes, this means that your Data Binding classes will actually be available prior to your first build. If you’ve used any annotation processors that generate classes that you rely on at runtime you’ll know that this is an absolute pain, so it’s nice to have these even if your build buggers up!

So let’s start off with a basic activity and see how we can start using Data Binding, this is just a basic activity that you might start a new project with.

class RepoActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_repo)
  }
}

First, we want to make sure we replace the framework setContentView with that of the Data Binding utility to retrieve a binding class that has been generated for us so that we can refer to in our programming code.

Also, as of AGP 3.1, Data Binding is life cycle aware, this means we need to inform the binding of our lifecycle owner so it can manage the subscription accordingly.

class RepoActivity : AppCompatActivity() {

    private lateinit var binding: ActivityRepoBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil
            .setContentView(this, R.layout.activity_repo)

        binding.setLifecycleOwner(this)
    }
}

As you can see, this is pretty basic, so how can we use Kotlin to improve upon it?

Kotlin allows us to use delegated properties, with delegate properties, we can create a getter and setter “template”, to define how we want to use a resource, and we can reuse it as many times as we like in different places, as long as it makes sense.

An example delegated property would be the Kotlin Lazy property delegate, where the value “computed” would only be computed once, and then the value “Hello” would be referred every time we then access it.

val value: String by lazy {
    println("computed!")
    "Hello"
}

Kotlin gives us two interfaces to extend from when we want to create our own delegate properties, the ReadOnlyProperty, and the ReadWriteProperty for value property (immutable) and variable properties (mutable) respectfully, and we implement the methods to provide the functionality.

So for our activity binding property delegate, we’ll want to make sure that we’re using an immutable property, so we’ll extend from the ReadOnlyProperty.

class ActivityBindingProperty<out T : ViewDataBinding>(
  @LayoutRes private val resId: Int
) : ReadOnlyProperty<Activity, T> {

  private var binding: T? = null

  override operator fun getValue(
    thisRef: Activity,
    property: KProperty<*>
  ): T = binding ?: createBinding(thisRef).also { binding = it }

  private fun createBinding(
    activity: Activity
  ): T = DataBindingUtil.setContentView(activity, resId)
}

The receiver of this property is the activity, meaning that this property delegate has to be created from an activity, so that we can use the reference activity to pass in the resource identifier for the layout, lazily instantiating our binding so that we will refer to the same one in the same way that we would use with the Lazy property.

Looking back at our activity, we can use an extension function to access our new delegate property, this is consistent with how we might do so with many other Kotlin delegate properties like Lazy or Observable so that it looks nice and consistent in our code.

class RepoActivity : AppCompatActivity() {

  private val binding by activityBinding<ActivityRepoBinding>(
    R.layout.activity_repo
  )

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding.setLifecycleOwner(this)
  }
}

fun <T : ViewDataBinding> FragmentActivity.activityBinding(
  @LayoutRes resId: Int
) = ActivityBindingProperty(resId)

Let’s introduce some architecture for our business behaviour!

At last years Google I/O conference, Google announced the architecture components, to start building framework applications quickly and easily, then again at this year’s Google I/O, they announced Jetpack Components so we can get even more.

This includes LiveData, ViewModel’s, and a sort of unofficial endorsement of using MVVM as a paradigm. Though this wasn’t an instruction to migrate your existing architecture over from MVP, or from MVI to MVVM, it was an indication that this is what you can use if you’ve got a fresh project or if you’re starting from scratch.

However, Data Binding works really with MVVM, because the view passively observes the ViewModel, and it doesn’t really matter if the view is there or not so we don’t have to worry about unregistering a listener or making sure our view is nullable or having all these because we can have many or no listeners for our ViewModel properties and the lifecycle-aware components will react accordingly.

The ViewModel state, is bound by view observation, and the view notifies the ViewModel of UI events, and user interactions, though I could probably talk all day about how everything works, this talk is about Data Binding, so let’s move on!

From the Activity as it was before, we need to actually apply our model to our binding, so we can access it from the view, and we’ll start writing binding expressions.

class RepoActivity : AppCompatActivity() {

  private val binding by activityBinding<ActivityRepoBinding>(
    R.layout.activity_repo
  )

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    binding.setLifecycleOwner(this)
    binding.model = ViewModelProviders
        .of(this, RepoViewModelFactory())
        .get(RepoViewModel::class.java)
  }
}

We access the ViewModel through the use of the ViewModelProviders of the AppCompatActivity and we provide a factory to instantiate the ViewModel if it’s not already been created.

You have to make sure to do this in Activity.onCreate method because otherwise, the activity will not have had the chance to restore its ViewModelStore which it retrieves using Activity.onRetainNonConfigurationInstance. If you’re interested about how this works, I recommend checking out the source code of the Activity.AppCompatActivity. But it’s a bit messy… as Android code usually is.

We can already improve this by introducing an extension function (I love extension functions ❤) to retrieve the ViewModel with a factory.

lass RepoActivity : AppCompatActivity() {

  private val binding by activityBinding<ActivityRepoBinding>(
    R.layout.activity_repo
  )

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    binding.setLifecycleOwner(this)
    binding.model = getViewModel(RepoViewModelFactory())
  }
}

inline fun <reified T : ViewModel> FragmentActivity.getViewModel(
  factory: ViewModelProvider.Factory =
    ViewModelProvider.NewInstanceFactory()
) = ViewModelProviders.of(this, factory).get(T::class.java)

You may notice here that I’m using the NewInstanceFactory; this is a default factory that will try and instantiate the ViewModel if it has a no-argument constructor. Though it’s generally not safe to rely on this, so I would advise not using it, but it’s there if you can be sure that you have no arguments in your constructor.

Additionally, if you want to access a property on your ViewModel later on, or in another place in your activity, you can use the Lazy delegate property to instantiate the ViewModel inside, then assign that to the binding, then you can access that later without having to worry about the nullability of your bindable binding since the generated code will have @Nullable annotation.

class RepoViewModel(service: RepoService) : ViewModel {

  val items: ObservableField<List<String>> = ObservableField()

  fun query(value: String) {
    service.fetch(value, items::set)
  }
}

Let’s take a look at our ViewModel, here’s a basic implementation, and we have the ObservableField, Data Binding provides ObservableField’s that we can use as a kind of wrapper to notify Data Binding of a property change.

There are observables for primitive types, like Boolean, Byte, Char, Short, Long, Int, and Parcelable, but mostly we’ll want to use ObservableField.

The Observable interface allows us to add and remove listeners to notify of a property change, and this extends from the BaseObservable which is implemented using the PropertyChangeRegistry, and we can extend from BaseObservable if we have a complex binding structure (this will be important later).

rainbow-tears

From AGP 3.1, we can use lifecycle aware components with LiveData and Data Binding, with the Data Binding classes as a LifecycleObserver, so we can create a MutableLiveData because we want to modify the value. It’s still a val, but it’s mutable, so it’s just the same as using mutableList.

class RepoViewModel(service: RepoService) : ViewModel {

  val items = MutableLiveData<List<String>>()

  fun query(value: String) { /* ... */ }
}

The nice thing about this is that because MutableLiveData is lifecycle-aware you have a bit more granular control over how you want to behave to notifications, and changes in your data, and also provide some additional benefits that you might be aware of, for example using Transformations.

class RepoViewModel(service: RepoService) : ViewModel {

  val items = mutableLiveData<List<String>>()

  fun query(value: String) { /* ... */ }
}

fun <T> ViewModel.mutableLiveDataOf() = MutableLiveData<T>()

You can also use an extension function to instantiate the ViewModel in a similar way to how you might use mutableListOf, listOf, or hashMapOf, as you would do with other Kotlin methods; it’s just the little things that make your code a little bit more readable, more concise, and consistent.

class RepoViewModel(private val service: RepoService) : ViewModel {

  val items = mutableLiveDataOf<List<String>>()

  fun query(value: String) {
    service.fetch(value) {
      items.value = it
    }
  }
}

Introducing some asynchronous operation to our ViewModel, with a service, and fetching a list of repositories from the service, GitHub for example, we’ll just assume that they’re String’s and we’ll just assume that there is appropriate asynchronous threading going on.

What if you want to show a progress bar to the user to let them know that there’s something going on in the background, so we don’t have a static view, and we want to have the progress bar disappear and appear, as and when is correct.

val loading = mutableLiveDataOf<Boolean>()

We can introduce a “loading” LiveData that we can observe, and set the value to true and false, before and after making the request. But how might we do achieve our view state in our layout?

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

  <data>

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

    <variable
        name="model"
        type="io.ashdavies.databinding.RepoViewModel" />

  </data>

  <ProgressBar
      android:visibility="@{model.loading ? View.VISIBLE : View.GONE}"
      ... />

</layout>

In our layout, we’ve got a binding to our view model loading property, and we want to make sure it’s visible or gone. The problem here is that we have a boolean which has two states, whereas we need to map this to a three-state visibility property. It’s fine doing it like this for now, but we can see how we can improve this shortly.

The progress bar visibility will be updated, as and when this value changes because the generated Data Binding class will listen to the changes. We also must import the view so that it knows to use View.Visible (it’s not clever enough to figure that out on its own 🤦‍♂)

But what if we wanted to display an empty state, in addition to our progress state, so say, to indicate to the user that their query didn’t return any results, or we just want to have a default empty state, so we introduce a TextView.

But we also want to make sure this is only visible when there are no items in our model, so we have this…

<TextView
      android:text="@string/activity_repo_empty"
      android:visibility="@{model.items.count == 0 ? View.VISIBLE : View.GONE}"
      ... />

But we also want to make sure our loading state isn’t conflicting with this as we don’t want our empty state to appear whilst our progress bar is visible as if it’s busy.

So we also combine this with the loading property and we’ll have a compound ternary expressing using the ampersand (which of course has to be escaped because its XML, and everybody loves XML…).

<TextView
      android:text="@string/activity_repo_empty"
      android:visibility="@{model.items.count == 0 &amp;&amp; !model.loading ? View.VISIBLE : View.GONE}"
      ... />

You can already see what’s going wrong here… it’s too complicated. We’re polluting our layout with business logic, it’s not easily testable, and it’s really putting people off.

It’s often one of the problems that I hear from people about wanting to use Data Binding, because they’re worried about having complex business logic in their layouts, and it’s discouraging because it doesn’t need to be that way.

How can we alleviate this?

class RepoViewModel(private val service: RepoService) : ViewModel {

  val items = mutableLiveDataOf<List<String>>()
  val loading = mutableLiveDataOf<Boolean>()
  val empty = mutableLiveDataOf<Boolean>()

  fun query(value: String) {
    loading.value = true
    empty.value = true

    service.fetch(value) {
      items.value = it
      empty.value = it.isEmpty()
      loading.value = false
    }
  }
}

We can create an additional property in our ViewModel so it represents the empty state, now it’s only modifying the empty state based upon the result of the model. It’s a little bit duplicitous because it’s the same as what’s happening in our view, but because it’s happening in our ViewModel it means it’s much more testable.

<TextView
      android:text="@string/activity_repo_empty"
      android:visibility="@{model.empty ? View.VISIBLE : View.GONE}"
      ... />

In our layout, we can use a bit of a cleaner expression, this is better, but we’re Kotlin developers, we can be better than better… we can be awesome 💪

Let’s introduce @BindingAdapters, @BindingAdapters allow us to create a different additional bit of specification to decide on how something should be bound. As mentioned earlier when we have a boolean with two states, we can now decide how to map those two states to a three-state visibility, so whether to use View.Invisible, View.Visible, or View.Gone and provide additional logic.

@BindingAdapter("goneUnless")
fun goneUnless(view: View, visible: Boolean) {
  view.visibility = if (visible) View.VISIBLE else View.GONE
}

We can create a binding adapter with the goneUnless property, and then decide if the view should be visible, or gone. We’ve moved this conditional operator that we had in our XML layout, into a testable Kotlin function.

@set:BindingAdapter("isVisible")
inline var View.isVisible: Boolean
  get() = visibility == View.VISIBLE
  set(value) {
    visibility = if (value) View.VISIBLE else View.GONE
  }

Furthermore, we can again make use of Kotlin extension functions; this is an example of an extension function made available to us in the Android KTX library so we can access the visibility of a view based upon whether it’s visible or gone.

This is accessible both programmatically, and in declarative layouts, unfortunately, the version in KTX library doesn’t have this annotation (since it intentionally excludes support libraries), but you can define your own, so now our layout is looking much more healthy.

<TextView
      android:text="@string/activity_repo_empty"
      app:isVisible="@{model.empty}"
      ... />

I think it’s much clearer, what’s going on, and we don’t have to worry too much about testing because if this is incorrect, or if it’s binding to the wrong property. If, for example, loading is actually a string instead of a boolean, then the application won’t compile, and we’re leaning on the compiler instead of relying on unit tests, so it’s much safer, and a much cleaner way of doing it.

Don’t forget to use the app namespace instead of android, because we’re using our own custom property!

So our layout looks good now, but we can improve the view model by introducing @Bindable, remember how I said that ObservableField extends from BaseObservable?

This implements that by creating a property change registry, and our bindable annotation is applied tot he properties in our generated binding class, which means it generates a BR resource identifier, similar to the resource identifiers that you’ll find an Android’s R class, allowing you to access strings, dimensions, and layouts, so you can also access bindable resources from your code.

You can now create an ObservableViewModel, an ObservableViewModel will just be a normal view model that extends from the Observable interface, and uses the implementation from the property change registry, so that the observers, know that something has changed. So we can either let it know that everything has changed or just an individual field.

abstract class ObservableViewModel : ViewModel(), Observable {

  private val registry: PropertyChangeRegistry =  PropertyChangeRegistry()

  override fun addOnPropertyChangedCallback(
    callback: Observable.OnPropertyChangedCallback
  ) = registry.add(callback)

  override fun removeOnPropertyChangedCallback(
    callback:Observable.OnPropertyChangedCallback
  ) = registry.remove(callback)

  fun notifyChange() {
    registry.notifyCallbacks(this, 0, null)
  }

  fun notifyPropertyChanged(fieldId: Int) {
    registry.notifyCallbacks(this, fieldId, null)
  }
}

Here is a simple ObservableViewModel implementation, you can see the two methods,addOnPropertyChangedCallback, and removeOnPropertyChangedCallback, will then allow Data Binding to listen to various properties.

We’ve added two functions as well, where we’re notifying the callbacks of change, and notifying the callbacks of property changes, calling the same method, but it’s referencing different parameters.

Using the parameter “0” (BR._all) means that all properties have changed and you need to rebind your entire dirty layout, or just an individual field, to let Data Binding know that only one field has changed, which is a little bit more concise.

class RepoViewModel(private val service: RepoService) : ObservableViewModel {

  @get:Bindable
  var items: List<String> = emptyList()
    private set(value) {
      notifyPropertyChanged(BR.items)
      field = value
    } /* ... */
}

We can now extend our view model from the newly created ObservableViewModel, and we can start creating bindable properties. So instead of using MutableLiveData, we can create an actual implementation, so we have the items, a variable which can be changed, and is much more reflective of its mutable status.

We have the notifyPropertyChange method call, to inform the Data Binding listeners, that this property has changed upon set. What’s really useful here as well, is that the setter is private, which means we can prevent any access from outside the view model, and have it really kind of localised.

But this is still a bit verbose, and there’s still a lot going on there, we can remove this boilerplate… we can do better.

Remember that we can create delegate properties in Kotlin, to define getters and setters of templates to reuse? We can do this with an observable property, so we can condense this getter and setter, into just a property value.

class BindableProperty<T>(
  initial: T,
  private val observable: ObservableViewModel,
  private val id: Int
) : ObservableProperty<T>(initial) {

  override fun beforeChange(
    property: KProperty<*>,
    oldValue: T,
    newValue: T
  ): Boolean = oldValue != newValue

  override fun afterChange(
    property: KProperty<*>,
    oldValue: T,
    newValue: T
  ) = observable.notifyPropertyChanged(id)
}

We want to extend the ObservableProperty from Kotlin so that we can listen for changes in this class, and we can react accordingly to “before” and “after” property change events.

This ObservablePropertyoverrides two functions, beforeChange, and afterChange. The former takes a boolean response to indicate whether the change is approved and whether the change is allowed to happen.

So we can now say, that I only want to change this if the old value does not equal the new value, which means that we’re not unnecessarily notifying the bindings of changes, and if you’re using two-way binding, it means that you can prevent infinite loops.

Ever had to search for the cause of a “StackOverflowException” on StackOverflow, you might know how difficult that is… it’s not easy.

We also have the afterChangemethod, which does the job of letting the observable know that this property has changed, and you can see in our constructor, that we’re taking in the initial value, the observable to notify, and the identifier of the field.

The identifier of the field will be the property from the BR class.

We can now apply this with an extension function.

class RepoViewModel(private val service: RepoService) : ObservableViewModel() {

  @get:Bindable
  var items by bindable<List<String>>(emptyList(), BR.items)
    private set

  /* ... */
}

fun <T> ObservableViewModel.bindable(
  initial: T,
  id: Int
): BindableProperty<T> = BindableProperty(initial, this, id)

We’ve got the annotation @get:Bindable on our items, to make sure that this bindable annotation is applied to the getter so that Data Binding knows this property will change or might change. We’re passing in the identifier BR.item, which is generated by our Data Binding annotation processor.

The annotation processor, by the way, does not need to be added to kapt or annotationProcessor in your build.gradle, it’ll be done by the Android Gradle Plugin.

There’s a really nice article from Aidan Mcwilliams who demonstrates this technique and builds upon it a little bit further, by using reflection to then check the BR.itemsreference, and it doesn’t pollute your stack trace if you have a problem at compile time. I chose not to demonstrate that here, so I recommend you checking it out online.

There are lots of different techniques like this that can be applied in Kotlin, and Data Binding, unfortunately, I can’t go into great detail about them all, but I’ve created a sample which you can access open-source on GitHub, it’s also available on the Google Play Store.

You can see lots of different techniques that I’ve used here to demonstrate how you can bind properties, so you can see the BindableProperty, you can see the ObservableField, MutableLiveData. You can also see how you can use MediatorLiveData to combine the value of two MutableLiveData sources, an operate on a lambda expression, to then have some custom behaviour.

Finally, these slides are available online, so you’ll be able to check out all the additional resources that I’ve used. I recommend going to check them out, there’s a blog post from George Mount on how to use Data Binding with a RecyclerView so you only need one adapter.

You can achieve a lot of things, and you can do so in a nice, testable fashion so that your declarative layouts are nice and clean.