Handling Lifecycle with View Binding in Fragments

Jamie Sanson
Trade Me Blog
Published in
3 min readJan 15, 2020

View Binding is an upcoming feature in Android, available in Android Studio 3.6 Canary 11+ which allows you to more easily interact with Views. It’s quick and easy to enable, and allows for type-safe view access. We’re likely all going to be using it in Fragments. Let’s explore how we can use it, in a safe and easy way!

View Binding in Fragments

Let’s take a look at the example from the View Binding Documentation. First we define some layout file:

result_profile.xml

<LinearLayout ... >   
<TextView android:id="@+id/name" />
<ImageView android:cropToPadding="true" />
<Button android:id="@+id/button"
android:background="@drawable/rounded_button" />
</LinearLayout>

This then generates a “binding” class, ResultProfileBinding. This class contains two fields, name and button, which refer to the views in our layout file. Nice!

Using this binding in a Fragment takes one more step — inflating it in onCreateView, and returning the root view. For example:

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}

Here we’re storing our binding class in a property so that we can access it later. As it turns out, due to the lifecycle of Fragment Views, this isn’t all we have to do.

Keeping Track of Lifecycle

When our view is destroyed we need to remember to clear our property, otherwise we’ll end up with a memory leak at best, and crashes at worst! The documentation recommends you do the following in your Fragments:

private var _binding: ResultProfileBinding? = null// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onDestroyView() {
_binding = null
}

This method works, but you can see how adding this to several different Fragment classes could get repetitive, and start to feel like boilerplate. Luckily for us, we can shorten this considerably in our Kotlin Fragments!

private var binding: ResultProfileBinding by viewLifecycle()override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}

We now no longer have to override onDestroyView, and we've decreased the number of properties we have to write! But wait.. viewLifecycle() isn't in AndroidX? How did we do that? By writing our own Property Delegate!

Let’s look at the full definition of viewLifecycle() and break it down:

fun <T> Fragment.viewLifecycle(): ReadWriteProperty<Fragment, T> =
object: ReadWriteProperty<Fragment, T>, LifecycleObserver {

// A backing property to hold our value
private var binding: T? = null

init {
// Observe the View Lifecycle of the Fragment
// * See Gist for full code *
this@viewLifecycle
.viewLifecycleOwnerLiveData
.observeLifecycles()
}

@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
// Clear out backing property just before onDestroyView
binding = null
}

override fun getValue(
thisRef: Fragment,
property: KProperty<*>
): T {
// Return the backing property if it's set
return this.binding!!
}
override fun setValue(
thisRef: Fragment,
property: KProperty<*>,
value: T
) {
// Set the backing property
this.binding = value
}
}

Wow, there’s a lot going on there. Let’s break it down.

  • viewLifecycle() is an extension function of Fragment, meaning we can use Fragment-related properties.
  • viewLifecycle() returns a ReadWriteProperty<Fragment, T>, an implementation of a property of a Fragment, which is of the generic type T.
  • We construct an anonymous class which implements ReadWriteProperty and LifecycleObserver, allowing us to listen to Lifecycle Events.
  • In the init block, we observe the Fragments viewLifecycleOwner. A Fragment’s View can be created and destroyed many times, so it may have more than one Lifecycle. AndroidX Fragment makes it easy for us, by including viewLifecycleOwnerLiveData, which emits the new Lifecycle Owner when the View is recreated.
  • Finally, when the View’s Lifecycle Owner changes, we observe the new Lifecycle. On the ON_DESTROYevent , sent when onDestroyView is about to be called, we null out our backing property.

This gives us the same behaviour as in the View Binding Documentation, but with much less code to cart around in our Fragments! You can find a full, more flexible example in this Gist, which you can drop in to your project and start using!

Thanks for reading! If you liked this article make sure to 👏 it, and follow me on Twitter! If you’re a fan of Mastodon check out Mammut — the Open Source client I’m building in my spare time.

--

--