Zhuinden / fragmentviewbindingdelegate-kt

[ACTIVE] A delegate for making managing the ViewBinding variable in a Fragment simpler.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to use onSaveInstanceState with Fragment binding

michaldrabik opened this issue · comments

commented

Hi @Zhuinden

So I stumbled upon some crashes related to the binding when trying to save instance state via

override fun onSaveInstanceState(outState: Bundle)

The case is quite simple:

class SomeFragmentFragment : Fragment () {
   
   private val binding by viewBinding(FragmentBinding::bind)
   
   override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    with(binding) {
      outState.putFloat("SOME_KEY", someTextView.translationY)
    }
  }

...
}

This results in (as Crashlytics reports)

Fatal Exception: java.lang.IllegalStateException: Should not attempt to get bindings when the Fragment's view is null.
       at com.***FragmentViewBindingDelegate.getValue(FragmentViewBindingDelegate.java:43)

Am I doing something wrong here or should this be somehow considered a bug? How can I properly save some values from the views and then restore? Should I just use findViewById in that cases? Cheers.

You need to store state-related values in a field, set those values for your view in onViewCreated + when those values are modified then the views should also be updated (see MutableLiveData.observe {} etc) and then you can store values of those fields in onSaveInstanceState.

This isn't really a bug of this library per say, a Fragment can enter onSaveInstanceState without having created a view, or after onDestroyView, when FragmentTransaction.attach/FragmentTransaction.detach are involved.

In fact, even without this library, you'd either just end up with an NPE in some cases, or just not store your state at all, or have a memory leak , etc. So extracting the state to fields like this has always been necessary.

class SomeFragmentFragment : Fragment () {
   
   private val binding by viewBinding(FragmentBinding::bind)

   private val someTranslationY = MutableLiveData(0.0f)

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       if(savedInstanceState != null) {
           someTranslationY.value = savedInstanceState.getFloat("SOME_KEY", 0.0f)
       }
    }

    override fun onViewCreated(...) {
        ...
        val binding = binding
        someTranslationY.observe(viewLifecycleOwner) {
            binding.someTextView.translationY = it
        }
    }
   
   override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    with(binding) {
      outState.putFloat("SOME_KEY", someTranslationY .value)
    }
  }

...
}