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