Android Data Binding Part 2: Observer pattern to the bone
In the previous part I've described typical problems we have to face when developing applications on Android. I've also highlighted that some of them may be mitigated when data binding API is utilized properly. It's time to dive into more details of how this promising API works.
At the root of many of solutions we find in today's APIs is a design pattern. In case of the discussed Android API it is Observer Pattern applied to the bone. In fact this particular pattern is so common and powerful that some languages and runtimes (C# with .NET, Objective-C and Swift on iOS and Mac) provide neat support for it. You may wonder why it is important in the context of Android data binding? The answer is simple yet easy to ignore - memory management. The following diagram depicts dependencies of an observer pattern's elements in the context of Android data binding:
On the right side we have the observable part producing notifications about changes - an object implementing
android.databinding.Observable interface. In the observer pattern events producer can have many events listeners -
OnPropertyChangedCallback implementations. Android data binding provides implementations of events listeners taking care of property values changes as well as collection elements changes.
Note that the
Observable object knows nothing about concrete
OnPropertyChangedCallback implementations (Dependency Inversion Principle at its best). However, at runtime the
Observable object will hold references to many
OnPropertyChangedCallbacks instances. If you look further down the diagram you'll see that in order for
OnPropertyChangedCallback implementations provided by Android data binding library to update the state of the view component a reference to it is required. This means that although the view model knows nothing about the view components at compile time they will reference them at runtime.
How Android data binding library aids memory management?
As stated above in a simple implementation we would have a lightweight and testable view model retain expensive view widgets - thus making it heavy. In the Android context it means that even though an
Activity got destroyed we could have other object still retaining its
Views, trying to update them and preventing them from being garbage collected. Not so nice, huh?
If you look closer at how Android data binding is implemented you'll immediately see that its
OnPropertyChangedCallback holds only weak references to views.
ViewDataBinding.WeakListener make sure that the
Observable object is not retaining views. This means that there's no need for a developer to manually stop data binding in an activity
onDestroy or a fragment
onDestroyView methods. If you use RxJava you probably know that this extra step is tedious and requires a great deal of attention and boilerplate code. You can find out more about the problem in at least couple of issues on GitHub: 1, 2.
Because Android data binding library takes extra steps to make sure a view model will not cause views to be retained we can detach a view model's lifecycle from that of an activity or a fragment. We now have an easy way to i.e. do a simple in-memory caching via singleton view models. More importantly, we can cross out at least one place when looking for a memory leak cause.
This article is cross-posted with my personal blog