suspend method inside runInTransaction block

Viewed 2020

I have a compilation error using the code below:

Suspension functions can be called only within coroutine body

Can someone explain to me why? What do I need to do to make it work (without using the @Transaction annotation)?

override suspend fun replaceAccounts(newAccounts: List<Account>) {
    database.runInTransaction {
        database.accountDao().deleteAllAccounts() // I have the error on this line
        database.accountDao().insertAccounts(newAccounts) // Here too
    }
}

@Dao
abstract class AccountDao : BaseDao<AccountEntity> {

    @Query("DELETE FROM Account")
    abstract suspend fun deleteAllAccounts()

}

Thanks in advance for your help

2 Answers

For suspend functions you should use withTransaction instead of runInTransaction

IO-bound and other long-running operations (such as database or API calls) are restricted from running in the main thread directly (which could otherwise cause your program to become unresponsive). Coroutines are like light-weight threads which run, asynchronously, within a thread.

I suggest reading through the Coroutines guide at https://kotlinlang.org/docs/reference/coroutines/coroutine-context-and-dispatchers.html

To answer your question, you need to setup a coroutine scope, and dispatcher thread for your coroutine to run on. The simplest is something like:

GlobalScope.launch(Dispatchers.IO) {
    replaceAccounts(newAccounts)
}

which would run your coroutine in the GlobalScope (the coroutine's "lifecycle" is bound to the lifecycle of the entire application), on the IO thread (a thread outside of the main thread which handles IO tasks).

EDIT I do like @IR42's answer. To build upon that, the usage of withTransaction in this case allows Room to handle the thread in which the database operations are performed on, and helps to limit concurrency to the database.

GlobalScope.launch(Dispatchers.Main) {
    replaceAccounts(newAccounts)
}

override suspend fun replaceAccounts(newAccounts: List<Account>) {
    database.withTransaction {
        database.accountDao().deleteAllAccounts() // I have the error on this line
        database.accountDao().insertAccounts(newAccounts) // Here too
    }
}

See more information on this article by one of Room's own: https://medium.com/androiddevelopers/threading-models-in-coroutines-and-android-sqlite-api-6cab11f7eb90

Related