suspender método dentro do bloco runInTransaction

Sep 01 2020

Eu tenho um erro de compilação usando o código abaixo:

As funções de suspensão podem ser chamadas apenas dentro do corpo da co-rotina

Alguém pode me explicar por quê? O que preciso fazer para que funcione (sem usar a @Transactionanotação)?

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()

}

Agradeço antecipadamente por sua ajuda

Respostas

9 IR42 Sep 01 2020 at 02:58

Para suspendfunções, use em withTransactionvez derunInTransaction

hightech Sep 01 2020 at 03:02

IO-bound e outras operações de longa duração (como chamadas de banco de dados ou API) não podem ser executadas diretamente no thread principal (o que poderia fazer com que seu programa parasse de responder). As corrotinas são como threads leves que são executados de forma assíncrona dentro de uma thread.

Eu sugiro a leitura do guia de corrotinas em https://kotlinlang.org/docs/reference/coroutines/coroutine-context-and-dispatchers.html

Para responder à sua pergunta, você precisa configurar um escopo de co-rotina e thread de despachante para que sua co-rotina seja executada. O mais simples é algo como:

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

que executaria sua co-rotina no GlobalScope (o "ciclo de vida" da co-rotina está vinculado ao ciclo de vida de todo o aplicativo), no encadeamento IO (um encadeamento fora do encadeamento principal que lida com as tarefas IO).

EDIT Eu gosto da resposta de @ IR42. Para construir sobre isso, o uso de withTransaction, neste caso, permite que a Room manipule o encadeamento em que as operações do banco de dados são realizadas e ajuda a limitar a simultaneidade no banco de dados.

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
    }
}

Veja mais informações sobre este artigo por um dos próprios Room: https://medium.com/androiddevelopers/threading-models-in-coroutines-and-android-sqlite-api-6cab11f7eb90