Android View Binding behind the scenes

Joao Foltran
CodeX
Published in
4 min readJul 31, 2022

--

“I love writing findViewByIds on my free time”, said no developer, ever. The reality is that view binding has greatly improved the bridge between layout files and Activities/Fragments. But I find that oftentimes developers use view binding, but don’t know exactly what’s going on behind the scenes. This article helps you understand how view binding is implemented so that you can fully take advantage of its features. Let’s get to it!

photo by pixabay

Basic usage

To get our feet wet, let’s take a look at an example layout and compare how we would interact with it from an Activity by using view binding and by not using it.

Our sample xml layout has a TextView and a Button components. With a small catch, the Button only exists in large screens, so we will define two layouts as follows:

The small (simplified) layout will be:

while the large (simplified) layout will be:

Without using view binding, setting a message in the TextView and a listener to the Button in MainActivity would look like this:

while using view binding, we could do something like this:

The Binding class

Any layout within a module where view binding is enabled gets its own binding class, which is a Java class optimized for interoperability with Kotlin. In a nutshell,

The binding class is used to inflate the layout, bind its children views and expose them as properties in a safe manner.

We will strip down the ActivityMainBinding class generated for the activity_main in the example above to see how it works.

Exposing Views

Regardless of the screen width of the device activity_main.xml runs on, we are guaranteed to have a TextView child of a ConstrainLayout, so our binding class exposes these views as non-nullable properties. However, the Button component only exists when the device has a large enough screen, so it is exposed as a nullable property for safe access in our Kotlin code.

Indeed, notice that we use the safe call operator to only set the click listener in case sampleButton is not null (i.e. the device’s screen is large enough). Whereas by using findViewById directly, we would always try to set the listener, thus getting a runtime error when running the app in a smaller screen device, since the activity_main layout for these does not include a Button.

In a nutshell,

View binding exposes one correctly typed property for each view that exists in a given layout, which helps us avoid errors when accessing the views before even building the app.

Besides helping avoid nullability issues, this approach also helps avoid other common mistakes that could happen by passing incorrect parameters to findViewById. For example, the build process would succeed, but the app would crash at runtime if:

  • We pass a non existing id to findViewById;
  • We pass an id that exists in another layout but not in the currently inflated one to findViewById;
  • We pass a valid id, but the wrong data type to findViewById.

Inflating the layout

Let’s take one step back now. We know how to access views using the binding object, but how did we get this object in the first place? We created it by calling the inflate static method from ActivityMainBinding passing our Activity’s layoutInflater.

The inflate method inflates the corresponding layout and binds the view. This is a crucial part of the class, since it checks for each child view’s nullity and guarantees that views that should not be null are not.

Great, we have inflated the layout, but we have not yet linked the inflated layout to our Activity. Notice that in the ActivityMainBinding constructor above, we pass down a reference of the inflated root view, which is accessible via the public root property.

Meaning we can now call setContentView(binding.root) from our Activity using Kotlin’s property access syntax.

Now a quick disclaimer: For simplicity, the codes above are a simplification from the actual generated code. The actual code optimizes for bytecode performance, but the logic remains equivalent.

Layouts inside of layouts

By now we know view binding helps reduce the amount of error-prone and boilerplate code for any xml layout. The great thing is that it also neatly supports layouts inside of layouts inside of layouts and so on…

For example, let’s say we have any xml layout in inner.xml. Then we could have another xml layout called outer.xml that includes inner:

Then the OuterBinding class would expose an InnerBinding object as a property and you could use all view binding features in both layouts, and any other recursively included layouts, in the same way.

Notice, however, that this is only possible because we added an id to the included layout. Just like view binding needs an id to generate properties for regular views (TextView, Button…) inside the binding class, it also needs ids to expose any included layouts as properties.

Thanks for reading! I hope the article shone some light into how view binding does its magic behind the scenes and how it provides a cleaner and less error-prone bridge between layouts and view components.

If you like the content, don’t forget to clap and follow for more!

--

--