(Mutable)LiveData is not getting observed on UI

Viewed 243

I went threw plenty of similar posts on SO, but still I was not able to find the reason why I am not able to observe the LiveData on the UI.

I am working on a project for learning purposes. What I am trying to do is to fetch a value from my Roomdatabase and display it on the UI. For the sake of clearness, I am showing only the relevant code snippets.

Dao.kt

@Query("SELECT maxValue FROM DoItAgainEntity WHERE id = :id")
    fun getMaxValue(id: Int): LiveData<Int>

Repository.kt

override fun getMaxValue(activityId: Int) = doItAgainDao.getMaxValue(activityId)

ViewModel.kt

private val mMaxValue = MutableLiveData<Int>()

override fun getMaxValue(id: Int): LiveData<Int> =
   Transformations.switchMap(mMaxValue) {
            repository.getMaxValue(id)
   }

Fragment.kt

viewModel.getMaxValue(activitiesAdapter.activities[position].id)

viewModel.getMaxValue(activitiesAdapter.activities[position].id).observe(viewLifecycleOwner, Observer {
     // THIS CODE IS NEVER CALLED
     Log.d("getMaxValueObserver",it.toString())
}

How can I reach the Int value provided in the LiveData object? I know, this is almost a newbie question, but could you please explain me what I am missunderstanding?

Thanks a lot.

3 Answers

You do not get your LiveData updated because you do switchMap on mMaxValue. But that LiveData never changes (as I can see from your sample).

You actually do not need the property private val mMaxValue = MutableLiveData<Int>().

You can simplify your function to:

fun getMaxValue(id: Int): LiveData<Int> = repository.getMaxValue(id)

SwitchMap

Returns a LiveData mapped from the input source LiveData by applying switchMapFunction to each value set on source.

There were a couple of steps that needed to be done to make this code working. Many thanks to @ChristianB for solving this problem. These were his suggestions:

  1. If the Entity has more than one column/field, how should Room know which value you want when LiveData<Int> is returned? So, the whole row (which is definded as an Entity object) must be returned instead, this would be the entity where the ID matches the parameter :id.

Dao.kt

@Query("SELECT * FROM DoItAgainEntity WHERE id = :id")
 fun getMaxValue(id: Int): LiveData<DoItAgainEntity>

  1. in the Repository you can filter/map for any field you actually need. Or even map it to a complete different (domain) object.

Repository.kt

override fun getMaxValue(activityId: Int): LiveData<Int> = doItAgainDao.getMaxValue(activityId).map { entity -> entity.maxValue }
  1. The switchMap(someLiveData) gets only called when the value of someLiveData changes, and then another liveData can be returned from it. But this was not my case. I just wanted to get a value from the LiveData, which goes down to my DAO. So switchMap is not needed here.

ViewModel.kt

 override fun getMaxValue(id: Int) = repository.getMaxValue(id)
  1. Finally, observing the LiveData on the UI works. The duplicate call to viewModel.getMaxValue(activitiesAdapter.activities[position].id) from the original question should be also removed.

Fragment.kt

viewModel.getMaxValue.observe(viewLifecycleOwner, Observer {
    // THIS CODE HAS NOW BEEN CALLED :)
    Log.d("getMaxValueObserver",it.toString())
    return@Observer
})
  1. Last but not least, If the Entity was updated like it was in my case, after creating the DB first, uninstall the app completely, do a proper DB version increase, so Room can run a migration on the DB! Clean & Rebuild the Project, Invalidate Caches / Restart.

In ViewModel

val mMaxValue = MutableLiveData<Int>()
override fun getMaxValue(id: Int){
    repository.getMaxValue(id).observeForEver{it ->
       mMaxValue.value = it
    }
}

And in the Activity or Fragment :

viewModel.getMaxValue(activitiesAdapter.activities[position].id)

viewModel.mMaxValue.observe(viewLifecycleOwner, Observer {
    // THIS CODE IS NEVER CALLED
    Log.d("getMaxValueObserver",it.toString())
}
Related