0

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)

mrbang
  • 366
  • 3
  • 12
  • Does this answer your question? [What is a NullPointerException, and how do I fix it?](https://stackoverflow.com/questions/218384/what-is-a-nullpointerexception-and-how-do-i-fix-it) – m0skit0 Aug 24 '23 at 07:30
  • Sorry but no... This is a specific question about Android / Android fragment lifecycles and not a general question about how to handle nulls / nullpointerexceptions. – mrbang Aug 24 '23 at 07:32
  • Is the `Activity` paused when the exception occurs? – David Wasser Aug 30 '23 at 13:34
  • After some debugging I found the reason. Synchronization with the main thread can take long time (e.g. 500ms). This is the problem: Fragment receives an event -> runOnUiThread is invoked and starts sync with main thread -> user switches to other fragment -> fragment is destroyed -> code inside runOnUiThread is invoked -> viewBinding is null -> app crash with NullPointerException. I havde decided to solve it by handling events in a ViewModel so I can get rid of runOnUiThread. Apparently I do not have enough points to add an answer to my own question :( – mrbang Aug 30 '23 at 18:07

0 Answers0