Data Binding Adapters And Dependency Injection- A Short Love Story
Data Binding and Dependency Injection are very hot topics for Android developers in the software industry nowadays. They once were a plus point in job descriptions but are now a mandatory skill requirement (rightly so).
Data Binding allows you to bind UI components in your layouts to data sources in your app using a declarative format rather than programmatically. In this sense, All views should be manipulated inside the XML layout file instead of the activity/fragment class.
This will not just wipe out redundant setViews
out of the activity/fragment classes but also using Data Binding’s BindingAdapters
you can create custom attributes for your UI component and reuse them everywhere in your project.
Basic Data Binding and Dependency Injection with Hilt is a pre-requisite as this article will not focus on the basics.
The goal of this article is to illustrate how can we gel Data Binding with Dependency Injection using Hilt-Android 🎯
The most simple and typical way of using Data Binding’s BindingAdapters
which we all have been using or at-least have had used in the past is by static methods.
A typical example of a BindingAdapter
would-be to load an image via the network. Rather than loading an image each time with Glide
inside your fragment or activity, you can simply create a custom attribute with BindingAdapter
and reuse it everywhere required.
First, you enable databinding
in your app-level build.gradle
and create a static or extension function and annotate it with @BindingAdapter(value = ["imageUrl"])
, anything that goes inside annotation parenthesis will be the name of your custom attribute which then you will be using in view XML.
Example:
TADAAA!! Our very first Binding Adapter is ready to be used.
But most of the time, the simple way is not the right way.
How nice it would be if we could make these static functions non-static? Converting them to non-static function won’t just make it easier to unit test them but also makes it possible to inject
dependencies wherever and whenever required using hilt-android.
Converting static BindingAdapters
to non-static functions is quite simple, using those non-static BindingAdapters
is also not very hard, but using them with Hilt
is a bit tricky. But not to worry, I gotcha.😉
Enough talking, let's get into some coding.
First, let’s wrap the static BindingAdapters
inside a class to make them non-static. Once they are instance function bound to a class, we can use constructor injection on the class and obtain any dependency required.
Now since our functions are non-static, accessing each custom attribute throws a FATAL Exception.
The exception is very self-explanatory. It says that DataBinding
requires a DataBindingComponent
if not using static BindingAdapter
functions.
DataBindingComponent:
This interface is generated during compilation to contain getters for all used instance BindingAdapters. When a BindingAdapter is an instance method, an instance of the class implementing the method must be instantiated.
Let’s look at what’s inside at our DataBindingComponent
So DataBindingComponent
created an abstract function with our class as return type which contains the BindingAdapters.
Documentation also says, If using Dagger 2 (Hilt is just a wrapper over Dagger 2), the developer should extend this interface and annotate the extended interface as a Component.
Ahh! Documentation can be so helpful (sometimes 😝).
Let’s create a custom Hilt
component
Four things that we need to create a custom component
, a scope
, a component
, a component builder
and an entry point
.
Relax! Take a deep breath… we are pretty much done.
Now we need to make sure these BindingAdapters
are provided to DataBinding
, so that we can to use them inside the layout files.
To do so, we set a default component
to DataBinding
which expects a DataBindingComponent
. Since our custom hilt
component (BindingComponent
) implements DataBindingComponent
, we can provide it to the DataBinding
with setDefaultComponent
function from DataBindingUtil.
That's it! 👐
The sample project with the complete implementation can be found on GitHub.
Did I get something wrong? Mention it in the comments. I would love to improve. 😊
Did you learn anything? Was it helpful? If yes, leave some claps 👏. It will make me happy 😊