Using coroutines scoped to ViewModel for IO


#1

So far as I can make out the following is a very common pattern for enabling coroutines in Android ViewModels.

class CoroutineScopedViewModel : ViewModel(), CoroutineScope {
    private val _job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + _job

    override fun onCleared() {
        super.onCleared()
        _job.cancel()
    }

    fun thisIsCalledFromDataBoundView() = launch {
        /* do some UI */
        withContext(Dispatchers.IO) {
            try {
                /* do some IO */
            } catch (error: IOException) {
                /* do some exception handling */
            }
        }
    }
}

Before I go about refactoring my project from the experimental (pre-1.0.0) version, I need to just have something clarified:

If the activity or fragment attached to the viewmodel is closed before the withContext(Dispatchers.IO) block is completed will that child job be canceled before completing? I.e. can this model be used for inserting data into my DB, or could some strange timing issue arise where the user hits back or otherwise causes the ViewModel owner to close that ends up losing the data?

To be clear I currently use GlobalScope.launch(Dispatchers.IO) (quickest conversion from 1.2.7x to 1.3.x) to write user entries into the SQLite DB (that I later sync with a server in the background), I just don;t want to inadvertently introduce a timing bug because I misunderstood something and converted to the model shown above.


#2

You’re correct, the withContext will be cancelled if the ViewModel goes away. You could take advantage of the fact that cancellation is cooperative, but that seems ugly to me.

I think if you want the DB write to succeed then it needs to be done in a different scope. GlobalScope is probably fine. So instead of withContext, use GlobablScope.launch(Dispatchers.IO). Actually I’d move that into a separate class, like some kind of repository class.


#3

That’s how I have it currently. I just see so many examples to the contrary (using a form similar to what is shown in my initial post) that it had me wondering if I’m doing something wrong.