Search code examples
androidandroid-fragmentskotlinandroid-roomandroid-viewmodel

Room insert lost when closing fragment and using ViewModelScope


I have followed the Android Room with a View tutorial but have changed it to a single-activity app with multiple fragments. I have a fragment for inserting records. The save button calls the viewmodel save method and then pops the backstack to return to the previous (list) fragment. Sometimes this works but often the insert does not occur. I assume that this is because once the fragment is destroyed, the ViewModelScope cancels any pending operations so if the insert has not already occured, it is lost.

Fragment:

private val wordViewModel: WordViewModel by viewModel

...

private fun saveAndClose() {
    wordViewModel.save(word)
    getSupportFragmentManager().popBackStack()
}

Viewmodel:

fun save(word: Word) = viewModelScope.launch(Dispatchers.IO) {
    repository.insert(word)
}

Repository:

suspend fun insert(word: Word) {
    wordDao.insert(word)
}

How do I fix this? Should I be using GlobalScope instead of ViewModelScope as I never want the insert to fail? If so, should this go in the fragment or the viewmodel?


Solution

  • One option is to add NonCancellable to the context for the insert:

    fun save(word: Word) = viewModelScope.launch(Dispatchers.IO + NonCancellable) {
        repository.insert(word)
    }
    

    The approach recommended and outlined in this post is to create your own application-level scope and run your non-cancellable operations in it.