How to use onSaveInstanceState with Fragment binding
michaldrabik opened this issue · comments
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)
}
}
...
}