web-dev-qa-db-fra.com

Kotlin coroutines `runBlocking`

J'apprends les coroutines Kotlin. J'ai lu que runBlocking est le moyen de relier le code synchrone et asynchrone. Mais quel est le gain de performances si le runBlocking arrête le thread d'interface utilisateur? Par exemple, je dois interroger une base de données dans Android:

    val result: Int
    get() = runBlocking { queryDatabase().await() }

private fun queryDatabase(): Deferred<Int> {
    return async {
        var cursor: Cursor? = null
        var queryResult: Int = 0
        val sqlQuery = "SELECT COUNT(ID) FROM TABLE..."
        try {
            cursor = getHelper().readableDatabase.query(sqlQuery)
            cursor?.moveToFirst()
            queryResult = cursor?.getInt(0) ?: 0
        } catch (e: Exception) {
            Log.e(TAG, e.localizedMessage)
        } finally {
            cursor?.close()
        }
        return@async queryResult
    }
}

L'interrogation de la base de données arrêterait le thread principal, il semble donc que cela prendrait le même temps que le code synchrone? Veuillez me corriger si je manque quelque chose.

10
Angelina

En fait, vous utilisez runBlocking pour appeler des fonctions de suspension dans du code "bloquant" qui autrement ne serait pas appelable là-bas ou en d'autres termes: vous l'utilisez pour appeler des fonctions suspend en dehors du contexte coroutine (dans votre exemple, le bloc passé à async est la fonction suspend). Aussi (plus évident, comme son nom l'indique déjà), l'appel est alors un appel bloquant. Donc, dans votre exemple, il est exécuté comme s'il n'y avait pas quelque chose comme async en place. Il attend (blocs de façon interruptible ) jusqu'à ce que tout dans le bloc runBlocking- soit terminé.

Par exemple, assumez une fonction dans votre bibliothèque comme suit:

suspend fun demo() : Any = TODO()

Cette méthode ne serait pas appelable depuis, par ex. main. Pour un tel cas, vous utilisez runBlocking puis, par exemple:

fun main(args: Array<String>) {
  // demo() // this alone wouldn't compile... Error:() Kotlin: Suspend function 'demo' should be called only from a coroutine or another suspend function
  // whereas the following works as intended:
  runBlocking {
    demo()
  } // it also waits until demo()-call is finished which wouldn't happen if you use launch
}

En ce qui concerne le gain de performances: en réalité, votre application peut être plus réactive au lieu d'être plus performante (parfois aussi plus performante, par exemple si vous avez plusieurs actions parallèles au lieu de plusieurs séquentielles). Dans votre exemple, cependant, vous bloquez déjà lorsque vous attribuez la variable, je dirais donc que votre application n'est pas encore plus réactive. Vous pouvez plutôt appeler votre requête de manière asynchrone, puis mettre à jour l'interface utilisateur dès que la réponse est disponible. Donc, vous omettez simplement runBlocking et utilisez plutôt quelque chose comme launch. Vous pouvez également être intéressé par Guide de programmation UI avec coroutines .

9
Roland

runBlocking est le moyen de relier le code synchrone et asynchrone

Je continue de me heurter à cette phrase et c'est très trompeur.

runBlocking est presque jamais un outil que vous utilisez en production. Il annule la nature asynchrone et non bloquante des coroutines. Vous pouvez l'utiliser si vous avez déjà du code basé sur la coroutine que vous souhaitez utiliser dans un contexte où les coroutines ne fournissent aucune valeur: pour bloquer les appels. Une utilisation typique est le test JUnit, où la méthode de test doit simplement s'asseoir et attendre la fin de la coroutine.

Vous pouvez également l'utiliser en jouant avec les coroutines, dans votre méthode main.

L'usage abusif de runBlocking est devenu si répandu que l'équipe Kotlin a en fait essayé d'ajouter une vérification rapide qui ferait immédiatement planter votre code si vous l'appeliez sur le thread d'interface utilisateur. Au moment où ils l'ont fait, il cassait déjà tellement de code qu'ils ont dû le supprimer.

22
Marko Topolnik