Android fragment DataBinding nonnull getter return null

Viewed 59

As per the android documentation, To get the data binding within a fragment, I use a non-nullable getter, but sometimes' When I try to access it again, after I'm wait for the user to do something, I receive a NullPointerException.

private var _binding: ResultProfileBinding? = null

private val binding get() = _binding!!

override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,
    savedInstanceState: Bundle?): View? {

    _binding = ResultProfileBinding.inflate(inflater)
    return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
   super.onViewCreated(view, savedInstanceState)

   setupViews()
}

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

private fun setupViews() {
   
   // On click listener initialized right after view created, all is well so far.
   binding.btnCheckResult.setOnClickListener {

      // There is the crash when trying to get the nonnull binding.
      binding.isLoading = true

   }
}

Does anyone know what the cause of the NullPointerException crash is? I'm trying to avoid not working according to the android documentation, and do not return to use nullable binding property (e.g _binding?.isLoading). Is there another way?

1 Answers

I can't explain why you're having any issue in the code above since a View's click listener can only be called while it is on screen, which must logically be before onDestroyView() gets called. However, you also asked if there's any other way. Personally, I find that I never need to put the binding in a property in the first place, which would completely avoid the whole issue.

You can instead inflate the view normally, or using the constructor shortcut that I'm using in the example below that lets you skip overriding the onCreateView function. Then you can attach your binding to the existing view using bind() instead of inflate(), and then use it exclusively inside the onViewCreated() function. Granted, I have never used data binding, so I am just assuming there is a bind function like view binding has.

class MyFragment: Fragment(R.layout.result_profile) {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val binding = ResultProfileBinding.bind(view)
            
        // Set up your UI here. Just avoid passing binding to callbacks that might
        // hold the reference until after the Fragment view is destroyed, such
        // as a network request callback, since that would leak the views.
        // But it would be fine if passing it to a coroutine launched using
        // viewLifecycleOwner.lifecycleScope.launch if it cooperates with
        // cancellation.
    }

}
Related