How to fix: Room DAO LiveData stops emitting across entire app after rapidly backgrounding and then resuming application

Viewed 58

I have several ViewModels that create observers on LiveData objects created via Room DAOs. These ViewModels merge various LiveData emissions into final ViewState LiveDatas, which are observed by their respective Fragments. These generally work without an issue, but there's a case in which almost all LiveData connected to Room suddenly break.

If I open my app, rapidly tap "Back" out of the application, and then rapidly resume the application, there's a 5-10% chance that most LiveData chains that interact with Room in any way become permanently stalled, as if the LiveData chain is not being observed. This happens everywhere on the app, even on fragments I haven't visited in the current session before the pathological behavior is exhibited.

After diving into the code, once the error state is reached, it seems like the Callable being handed off to Room's invalidation tracker is never being called. As an experiment, I created an observeForever on the profileStream to see if I could engage a different codepath. This resulted in the Callable being executed, but it returns stale data. ObserveForever being an unsuitable solution anyway, I didn't want to further debug this option.

Suspicious of lifecycle-related issues, I swapped out "this" on my observe calls for the Fragments' viewLifecycleOwners. This didn't seem to change anything.

viewModel.viewState.observe(viewLifecycleOwner, Observer { render(it) })
VIEWMODEL

init {
  val profileStream = profileRepo.getProfile(id)
  viewState.addSource(mapPhotoStream()) { photos -> viewState.value = 
  currentViewState().copy(photos = photos) }

  viewState.addSource(profileStream) { profile ->
    if (profile.name == null) {
      Log.w(TAG, "Name of a profile should not be null. 
      UserId=${profile.userId}")
    }
    viewState.value = currentViewState().copy(
      name = profile.name ?: ""
    )
  }
}
FRAGMENT

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  setupBottomSheetBehavior()
  viewModel.viewState.observe(viewLifecycleOwner, Observer { render(it) })
}

0 Answers
Related