• Data Binding — Lessons Learnt
  • Originally by Chris Banes
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: Mirosalva
  • Proofread by DevMcryYu

The Data Binding library (hereinafter referred to as the “DB library”) provides a flexible and powerful way to bind Data to the UI. But to use the cliche “with great power comes great responsibility”, just because you use data binding doesn’t mean you can avoid being a good UI developer.

I’ve been using the Data Binding library for Android development for the past few years, and this article will give you some details about what I’ve learned along the way.

Use bindings whenever possible

A custom Binding Adapter is a good way to easily provide custom functionality to View controls. Like many developers, I delved a little deeper into Binding Adapter and ended up with a set of classes for 15 different uses of adapters.

The worst practices are adapters that generate formatted strings and set them to TextViews controls, which are usually only used in the same layout file:

While this may seem clever, there are three major downsides:

  1. The process of optimizing them is too painful. Unless you organize your code very well, you may have a large file containing all the adapter methods, which goes against the principle of code cohesion and decoupling.

  2. You need to use instrumentation tools to do testing. By definition, your Binding Adapters do not return values. They take an input parameter and set the view’s properties. This means that you must use instrumentation to test your custom logic, which can make testing slow and difficult to maintain.

  3. Customizing binding Adapter code is (usually) not the best option. If you look at the built-in text binding [see here], you’ll see that a lot of checking has been done to avoid calling TextView.settext (), thus saving wasted layout detection. I feel like I’m stuck with the idea that the DB library will automatically optimize my view updates. It does, but only if you use a carefully optimized built-in Binding Adapter.

Instead, abstract the logic of your method into clusters (I call them text creator classes) and pass them to a binding. This way you can call your text creator class and use a built-in View Binding:

This allows us to gain efficiency from the built-in binding process, and we can easily unit test the code that creates the formatted string.

Make your custom Binding adapter efficient

If you do need to use a custom adapter because the functionality you need doesn’t exist, make it as efficient as possible. I mean use all the standard Android UI optimizations: avoid triggering measurement/layout operations as much as possible.

This can be as simple as checking what views are currently in use and what you have set up. Here is an example of a standard ImageView adapter that we have re-implemented for Android :drawable:

Unfortunately, views don’t always show the state we need to check. Here is an example of setting the maximum row to switch on a TextView. It switches by changing the maxLines property of the TextView and a delayed layout transition (Android.view.viewGroup).

Previously the Binding adapter was simple and always had the maxLines attribute and a click-listening object set. TextView will always fire a layout after setMaxLines() is called, which means that every time a Binding Adapter is started, a layout will fire.

Let’s change that. Since this function is completely separate from TextView (we just call setMaxLines() with different values when we click), we need to store the reference as the current state. Fortunately, the “DB library” provides a manual way to receive state in the Binding Adapter. By providing parameters twice: the first parameter receives the current value, and the second parameter receives the new value.

So here we just compare the current and new collapsedMaxLines values. If the value actually changes, we call methods like setMaxLines().

Edit click: Thanks to Alexandre Gianquinto for mentioning “Double Parameters” in the comments.

Be careful about the variables you provide

I’ve been slowly redesigning Tivi, using something like MVI and normalizing it with the great MvRx library. What this means in practice is that my Fragment/View subscribes to a ViewModel object and receives an instance of ViewStates. These instances contain all the necessary state to display the UI.

Here is a sample class showing Tivi (links) :

As you can see, it’s just a simple data class that contains all the detailed UI elements that the UI needs to display on a TV show interface.

Sounds like the perfect option to pass our Data Binding instance object and let our Binding expression update the UI, right? Well, this does work, but there are a few caveats, and this is due to how “DB libraries” work.

In the Data Binding you declare the input via the

tag, and then reference the input variables in the View property when writing the binding expression. When any dependent variable changes, the DB library will run your binding expression (and then update the view). This change detection is a great optimization that you can get for free.

So back to my scene, my layout ended up looking like this:

So I ended up getting a global ViewState instance of all the UI states, and as you can imagine those states change quite often. Any slight change in the UI state generates a new ViewState that is passed to our data binding instance.

So what’s the problem? Since we have only one input variable, all binding expressions will reference the variable, which means that the “DB library” will not be free to choose which expression to run. In practice, this means that all binding expressions run every time a variable change (no matter how small) occurs.

This issue has nothing to do with the MVI point, especially since it is just a combined state artifact, used in conjunction with a data binding.

So what can you do?

An alternative is to explicitly declare each variable in ViewState in the layout and then explicitly pass the value in the combined state instance, as follows:

This obviously allows developers to maintain and synchronize more code, but it does mean that the “DB library” can be optimized for which expressions to run. I would recommend using this pattern if your UI state doesn’t change very often (maybe a few times during creation) and the number of variables is small.

I personally have been using a single variable in my layout, passing in my ViewState instance, and relying on our view bindings to run properly. That’s why it’s important to make view binding efficient.

The other thing to note is that Tivi isRecyclerViewHeavy users, andEpoxy 和 Data Binding”Means inDiffUtilThere will be some additional changes related to the calculation. So if your UI is also made up of a lot of RecyclerView, you can easily get computing optimizations like the one described above.

Small step iteration

Hopefully, this article has highlighted some of the little things you can do to optimize your data binding implementation. Understanding the inner workings of the “DB library” can help you improve data binding efficiency and improve your UI performance.

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.