Home » Android » android – Observing database object changes made from the background thread-Exceptionshub

android – Observing database object changes made from the background thread-Exceptionshub

Posted by: admin February 26, 2020 Leave a comment

Questions:

I’ve met an issue with observing data in my app.

For the testing purposes, I have an activity with a single text view where I show the user’s name. This is the code:

@Entity(tableName = "User")
data class User(
    var name: String,
    var surname: String,
    @PrimaryKey(autoGenerate = true)
    val internalID: Long = 0)

in the dao I’ve got just one method:

@Query("SELECT * FROM User WHERE surname LIKE :surname")
abstract suspend fun getUserForSurname(surname: String): User

in the activity onCreate’s method:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    val model = ViewModelProvider(this).get(MainViewModel::class.java)
    binding.viewmodel = model
    model.user.observe(this, Observer {
        binding.textTest.setText(it.name)
    })
}

and finally, view model:

class MainViewModel(application: Application) : AndroidViewModel(application) {

    private val surname = "Doe"
    val user: MutableLiveData<User> = MutableLiveData()
    private val userDao: UserDao =
        MyRoomDatabase.getDatabase(application).clientDao()

    init {
        viewModelScope.launch {
            user.value = userDao.getUserForSurname(surname)
        }
    }
}

That specific user’s name is changed in the background thread. When I check for the value in db itself, the name is indeed different. After restarting activity, the text view is changed too. In other words: the db value is changed but the observer is never called. I know that I am asking for the value only once during viewmodel’s init method and it may be a problem. Is it possible to see the actual change without restarting activity?

How to&Answers:

I suggest you have a look at this Codelab

Room exposes different wrappers around the returned Entities such as:

  • RxJava
  • Flow Coroutines
  • LiveData

So you can changed your Dao as such:

@Query("SELECT * FROM User WHERE surname LIKE :surname")
abstract fun getUserForSurname(surname: String): LiveData<User>

The above means that any changes to the user entry will emit an observation to the listeners of the LiveData.

ViewModel

class MainViewModel(application: Application) : AndroidViewModel(application) {

    private val surname = "Doe"
    lateinit val client: LiveData<User>
    private val userDao: UserDao =
        MyRoomDatabase.getDatabase(application).clientDao()

    init {
        viewModelScope.launch {
            user = userDao.getUserForSurname(surname)
        }
    }
}

Read more at:
https://developer.android.com/training/data-storage/room/index.html

Disclaimer: Didn’t test the above solution but it should give you an idea.

EDIT: Ideally LiveData should only be used in your view model as they were designed for such cases and not to observe DB transactions. I will suggest to replace the Dao’s with Coroutine’s Flow and use the extension to convert to LiveData.