I have a fragment that uses viewbinding through eventhandlers. The eventhandlers are setup and taken down in onResume() and onPause(). In rare cases accessing the view through events results in a crash caused by the view not beeing initialized (NullPointerException in getViewBinding). The events comes from a background job running in a foreground service this is the reason for requireActivity().runOnUiThread{}.
I consider making the _viewBinding! optional and introduce a nullpointercheck. But this would require nullpointer checks many different places. An other option can be to introduce a viewmodel with LiveData objects but I would like to understand why this is not working and a suggestion of the best fix.
This is how it looks - stripped for all irrelevant code. I expected this to change the state of the button in the view but periodically get a NullpointerException in the line viewBinding.manualConfigurationButton.visibility = View.VISIBLE
@AndroidEntryPoint
class LaunchScreenFragment : Fragment(), IEnvConfigurationObserver {
private var _viewBinding: FragmentLaunchscreenBinding? = null
private val viewBinding get() = _viewBinding!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
_viewBinding = FragmentLaunchscreenBinding.inflate(inflater, container, false)
return viewBinding.root
}
override fun onResume() {
updateUI(false)
envConfigurationRepository.addObserver(this)
super.onResume()
}
override fun onPause() {
envConfigurationRepository.removeObserver(this)
super.onPause()
}
override fun onDestroyView() {
_viewBinding = null
super.onDestroyView()
}
private fun updateState(isEnvironmentConfigurationInvalid: Boolean) {
requireActivity().runOnUiThread {
if (isEnvironmentConfigurationInvalid) {
viewBinding.manualConfigurationButton.visibility = View.VISIBLE
}else{
viewBinding.manualConfigurationButton.visibility = View.GONE
}
}
}
override fun envConfUpdated(isValid: Boolean) {
updateState(isValid)
}
}
This is the exception we see when the app crashes:
java.lang.NullPointerException at com.systematic.flow.general.launchscreen.ui.LaunchScreenFragment.getViewBinding(LaunchScreenFragment.kt:118) at com.systematic.flow.general.launchscreen.ui.LaunchScreenFragment.updateState$lambda$2(LaunchScreenFragment.kt:236) at com.systematic.flow.general.launchscreen.ui.LaunchScreenFragment.$r8$lambda$7D38a3ZrFZSEHHlFdB43Rl-ao3A(Unknown Source:0) at com.systematic.flow.general.launchscreen.ui.LaunchScreenFragment$$ExternalSyntheticLambda3.run(Unknown Source:2) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8757) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)