2

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?

Moti
  • 21
  • 1
  • Is this the exact code you're using, or did you cut some stuff out to simplify it before posting. The code above looks safe. A View's click listener can only be called while it is on screen, which must logically be before `onDestroyView()` gets called. By the way, ViewBinding and DataBinding are not exactly the same thing. Your question talks about DataBinding, but you linked the documentation for ViewBinding. However, the way you import them in a Fragment can be done the same way. – Tenfour04 Jun 16 '22 at 14:29
  • Yeah, it's the exact same code, I noticed that the documentation is for ViewBinding, But I guess like you said, it's should be in the same way for DataBinding . – Moti Jun 16 '22 at 14:40

1 Answers1

2

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.
    }

}
Tenfour04
  • 83,111
  • 11
  • 94
  • 154