5

I am trying to understand the implementation of view binding in a fragment and I found that it is different from an activity.

In an activity:

private lateinit var binding: ResultProfileBinding

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ResultProfileBinding.inflate(layoutInflater)
    val view = binding.root
    setContentView(view)
}

In a fragment:

private var _binding: ResultProfileBinding? = null
private val binding get() = _binding!!

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

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

Now my question is, why do we have binding and _binding in the fragment? What is this line is doing and what is it's purpose?

private val binding get() = _binding!!
Henry Twist
  • 5,666
  • 3
  • 19
  • 44
Saraladewi
  • 117
  • 2
  • 8

2 Answers2

4

In the second example, the _binding property is nullable so as to allow a state before it has been 'initialised'. Then the binding property has a getter to provide convenient access, given that the backing field (_binding) has been initialised.

The specific line you're referring to means that when you try to access binding, it will return _binding. However the null assertion operator (the !!) adds the extra assertion that _binding isn't null.

Really all that you've done is created an analogue of the lateinit property, and actually if you look at the decompiled bytecode of a lateinit declaration, they amount to the same.

However, as @Tenfour04 pointed out, the subtle difference here is that the second approach allows you to set the backing field back to null, whereas you can't do this with a lateinit property. When you use a binding in a fragment, it's recommended to null out the binding in onDestroyView in order to avoid memory leaks, so this is why they've gone with this approach in a fragment.

Henry Twist
  • 5,666
  • 3
  • 19
  • 44
  • if it is said to be two different approaches.. can we follow approach 1 in fragment as well? @Henry – Saraladewi Jun 25 '21 at 03:13
  • in what cases approach 2 will be appropriate ? can u give some example please @Henry Twist – Saraladewi Jun 25 '21 at 03:33
  • The difference between this and lateinit is that this lets you change the value back to null, which the Android documentation recommends doing when the fragment’s view is destroyed so you aren’t temporarily leaking the views. – Tenfour04 Jun 25 '21 at 03:46
  • 1
    Since Activities are destroyed when their views are destroyed there’s no concern about leaking the views so the more concise lateinit can be used. So the reason different strategies were used in the documentation has everything to do with Activities vs. Fragments. – Tenfour04 Jun 25 '21 at 04:03
  • @Tenfour04 If we create a getter to access a nullable atribute with `!!`. Is it not as dangerous as using lateinit? In a project I'm using lateinit and replacing fragments inside one activity and didn't have issues. Do you mind giving an example about why it isn't safe? Maybe making a request that tries to update the UI after switching fragments? – JCarlosR Oct 01 '21 at 20:24
  • 2
    `lateinit` is safer than the separate `!!` enforcing property because you can't accidentally get a NullPointerException. With the separate `!!` enforcing property, you must be careful not to use it in a callback that might get called after the fragment is destroyed. Google recommends using the `!!` enforcing property so you can change the backing binding property back to null when the view is destroyed. This prevents the views in the binding from being *temporarily* leaked during the time a fragment is detached. – Tenfour04 Oct 01 '21 at 20:39
  • 1
    In practice, the time a fragment is in detached state is very short--less than a second. Either it is about to be destroyed, or it is about to be reused after a screen rotation. So I think many experienced developers would say the `!!` enforcing property is overkill. It's much simpler to use `lateinit`, and it's also theoretically safer, although if you're accessing views during a detached state, I would say you're being sloppy. @JCarlosR – Tenfour04 Oct 01 '21 at 20:41
0

In fragments the View is instantiated in onCreateView() method call. But onCreateView() is not the first method that gets called when a fragment is created, which means that the View could be null before the onCreateView() method call, and if the View will be null, so will be the _binding property. In order to avoid nullPointerException we are using a variable "binding" to get the latest non-null(!! checks) value of _binding.

private val binding get() = _binding!!

This declaration has an inbuilt get() method, that updates the value of binding on each call that we make to binding to return the current non-null value of _binding because _binding!!, will throw an exception if _binding has a null value.

Abhishek Guru
  • 483
  • 4
  • 8