Android Data Binding: Observability

Keeping the UI Up-to-Date

George Mount
Android Developers
Published in
3 min readSep 22, 2016

--

Android Data Binding is a very easy way to insert data into the UI. So far, I’ve only shown how to do this using plain-old-java objects (POJOs). However, when the data is updated, there’s no notification to update the UI. That’s not great when the server sends down an update and you want the user to see the change. Android Data Binding provides several ways for you to keep your data synced with the UI.

POJO

I have a plain-old-Java object, “Foo”, with a single property, “bar” that I bind to my UI:

public class Foo {
private int bar;

public int getBar() {
return bar;
}

public void setBar(int bar) {
this.bar = bar;
}
}

I want the UI to change every time I call “setBar,” so let’s see how to make data binding keep up with the changes:

Observable

The most flexible mechanism is to have your object implement the Observable interface, which has these two methods:

void addOnPropertyChangedCallback(OnPropertyChangedCallback c);

void removeOnPropertyChangedCallback(OnPropertyChangedCallback c);

The OnPropertyChangedCallback must be notified when your fields have changed:

public abstract void onPropertyChanged(Observable sender,
int propertyId);

The class must also annotate any getters that should be observed with “@Bindable.”

@Bindable
public int getBar() {
return bar;
}

This tells the data binding framework that the property is observable and also generates an identifier in the “BR” class in your application package. This class is similar to the “R” class, but for the binding resources that you’ll use for the propertyId.

Android data binding gives you the PropertyChangeRegistry class to make it easier to track listeners:

public class Foo implements Observable {
private PropertyChangeRegistry registry =
new PropertyChangeRegistry();
private int bar;

@Bindable
public int getBar() {
return bar;
}

public void setBar(int bar) {
this.bar = bar;
registry.notifyChange(this, BR.bar);
}

@Override
public void addOnPropertyChangedCallback(
OnPropertyChangedCallback callback) {
registry.add(callback);
}

@Override
public void removeOnPropertyChangedCallback(
OnPropertyChangedCallback callback) {
registry.remove(callback);
}
}

BaseObservable

If your model objects can extend a base class, the BaseObservable makes it easier by implementing the Observable interface for you. You only need to annotate the properties and notify the change:

public class Foo extends BaseObservable {
private int bar;

@Bindable
public int getBar() {
return bar;
}

public void setBar(int bar) {
this.bar = bar;
notifyPropertyChanged(BR.bar);
}
}

ObservableField

Observable and BaseObservable are a little complicated for simple usage, so we introduced ObservableField and its primitive counterparts. To use ObservableFields, just declare public final fields in your class. I’ll have two properties this time to demonstrate primitive and reference types:

public class Foo {
public final ObservableInt bar = new ObservableInt();
public final ObservableField<String> baz =
new ObservableField<>();
}

You can use these as normal properties in binding expressions:

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{foo.baz}"
/>

While the binding expressions remain attractive, Java requires calling the setters and getters:

foo.bar.set(age);
String name = foo.baz.get();

ObservableMap

There are times when data is not well defined. You may still be prototyping and the data format from the server is still in flux and you don’t want to create objects for them. ObservableMap and its implementation, ObservableArrayMap, were created for these more nebulous data structures.

With the ObservableMap, you can access values in your map like any normal map and the UI will be updated when changes occur. For example:

<data>
<variable name="product"
type="android.databinding.ObservableMap&lt;String, Object&gt;"
/>
</data>

<!-- ... -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@{product["name"]}'
/>

And you’d use the ObservableMap in your code:

ObservableMap<String, Object> product = new ObservableArrayMap<>();
binding.setProduct(product);
product.put("name", "Golf Ball");

Any time the product object changes, it will update the UI.

Conclusion

Many UIs don’t need to be kept informed of data changes, so you won’t have to add observability to your entire model. On the other hand, it can be nice to have the UI automatically update when your server sends down new data or the user makes a change in one part of the UI and the rest of the UI updates on its own.

Choose whichever method for observability you prefer. Most will extend BaseObservable or use ObservableFields, but the others also have their uses.

--

--