Coroutine Builder — des fonctions de Kotlin Coroutines qui créent et lancent des coroutines, définissant leur mode d'exécution. Les constructeurs launch, async, runBlocking et produce couvrent différents scénarios : des tâches d'arrière-plan aux calculs parallèles avec retour de résultat. Selon JetBrains, 2024, Coroutine Builder est la base du modèle de coroutines, offrant une concurrence structurée et une gestion du cycle de vie.
Points clés
Coroutine Builder est une fonction d'extension de Kotlin qui prend un CoroutineScope et un bloc suspend, créant et lançant une nouvelle coroutine. Chaque constructeur définit comment la coroutine sera exécutée : avec ou sans retour de résultat, avec blocage de thread ou de manière asynchrone. Les constructeurs sont les points d'entrée dans le modèle de coroutines du langage.
Tous les constructeurs fonctionnent via CoroutineScope, qui gère le cycle de vie des coroutines filles. Lorsqu'un scope est annulé, toutes les coroutines lancées via celui-ci sont automatiquement annulées — c'est le principe de la concurrence structurée. Cette approche empêche les fuites de coroutines et garantit une terminaison prévisible.
import kotlinx.coroutines.*
fun main() = runBlocking {
// Les constructeurs s'exécutent dans CoroutineScope
val job = launch {
delay(1000L)
println("Monde !")
}
println("Bonjour,")
job.join()
}
Kotlin fournit quatre constructeurs de coroutines intégrés : launch, async, runBlocking et produce. Chacun a son propre type de retour et son domaine d'application. Pour le développement mobile Android, les principaux sont launch et async — ils fonctionnent de manière non bloquante et s'intègrent aux composants architecturaux.
| Constructeur | Type de retour | Blocage de thread | Scénario |
|---|---|---|---|
| launch | Job | Non | Tâches fire-and-forget |
| async | Deferred<T> | Non | Calculs parallèles |
| runBlocking | T | Oui | Tests, fonction main |
| produce | ReceiveChannel<E> | Non | Diffusion (obsolète) |
Chaque constructeur accepte des paramètres supplémentaires : CoroutineStart (stratégie de lancement), CoroutineContext (répartiteur, exceptions) et un bloc de code nommé. Par défaut, la coroutine démarre immédiatement (CoroutineStart.DEFAULT).
launch est le constructeur le plus utilisé dans le développement Android. Il lance une coroutine qui ne retourne pas de résultat, et retourne un objet Job pour gérer son cycle de vie. C'est le choix idéal pour les opérations qui nécessitent seulement un effet secondaire : sauvegarde en base de données, envoi d'analyses, mise à jour de l'UI.
Le constructeur launch accepte CoroutineScope, un CoroutineContext optionnel et un bloc suspend. Le Job retourné permet d'annuler la coroutine, d'attendre sa fin ou de vérifier son statut.
val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
val job: Job = scope.launch(CoroutineStart.LAZY) {
val data = fetchFromNetwork()
saveToDatabase(data)
}
job.start()
job.join()
Le paramètre CoroutineStart.LAZY reporte l'exécution jusqu'à un appel explicite à start() ou join(). Ceci est utile pour l'initialisation différée et l'exécution conditionnelle. Pour l'exécution immédiate standard, on utilise CoroutineStart.DEFAULT ou on omet le paramètre.
async est un constructeur qui retourne Deferred<T> — une promesse asynchrone d'un résultat. L'appel à await() suspend la coroutine jusqu'à l'obtention du résultat, sans bloquer le thread. C'est le mécanisme principal pour les tâches parallèles dans les coroutines Kotlin.
async est particulièrement efficace lorsque vous devez exécuter plusieurs opérations indépendantes simultanément. Contrairement aux appels séquentiels de fonctions suspend, async lance les coroutines en parallèle, réduisant le temps d'exécution total.
suspend fun fetchUserData(): UserData {
val deferred1 = CoroutineScope(Dispatchers.IO).async { api.getProfile() }
val deferred2 = CoroutineScope(Dispatchers.IO).async { api.getSettings() }
val deferred3 = CoroutineScope(Dispatchers.IO).async { api.getNotifications() }
return UserData(
profile = deferred1.await(),
settings = deferred2.await(),
notifications = deferred3.await()
)
}
Deferred hérite de Job, donc async prend en charge toutes les opérations du cycle de vie : annulation, attente de fin, gestion des exceptions. Lorsqu'un scope est annulé, les coroutines Deferred filles sont automatiquement annulées.
runBlocking est le seul constructeur qui bloque le thread actuel jusqu'à la fin de la coroutine. Il crée un nouveau CoroutineScope et lance la coroutine passée, bloquant le thread appelant. Il est utilisé dans les points d'entrée main(), dans les tests et lors de l'intégration avec du code bloquant.
runBlocking est justifié dans trois scénarios : le point d'entrée de l'application (main), les tests unitaires de fonctions suspend et l'intégration avec des bibliothèques basées sur des callbacks où suspend ne peut pas être utilisé. Dans le code Android de production, l'utilisation de runBlocking sur le thread principal n'est pas recommandée.
class CoroutineTest {
@Test
fun `test suspend function`() = runBlocking {
val result = mySuspendFunction()
assertEquals("expected", result)
}
}
Pour les tests, il est recommandé d'utiliser kotlinx-coroutines-test avec TestCoroutineDispatcher au lieu de runBlocking — cela offre un contrôle du temps et évite les blocages dans l'environnement de test.
Le choix du Coroutine Builder dépend du résultat retourné et du scénario d'exécution. Si l'opération ne nécessite pas de retour de données — utilisez launch. Si vous avez besoin du résultat d'une opération asynchrone — utilisez async. runBlocking ne doit être utilisé que pour le pont, et remplacez produce par Flow pour les flux réactifs.
Dans les projets Android utilisant Kotlin Coroutines, la paire principale de constructeurs sont launch et async. launch est utilisé dans ViewModel et UseCases pour lancer des coroutines, tandis qu'async est utilisé pour les requêtes parallèles au réseau ou à la base de données. Les bibliothèques modernes (Ktor, Room) supportent déjà les fonctions suspend, ce qui minimise le besoin d'utilisation directe d'async.
Questions fréquentes
launch retourne Job et ne retourne pas de résultat d'exécution, tandis qu'async retourne Deferred<T> — un objet à partir duquel le résultat peut être obtenu via await(). launch est utilisé pour les opérations fire-and-forget, async pour les tâches qui retournent des données.
Déconseillé. runBlocking sur le thread principal provoque une ANR et bloque l'UI. Utilisez lifecycleScope.launch dans Activity et Fragment — c'est une solution intégrée sans blocage.
Le constructeur launch retourne un objet Job, qui permet de contrôler le cycle de vie de la coroutine : annuler (cancel), attendre la fin (join), vérifier le statut (isActive, isCompleted, isCancelled).
Deferred<T> est une promesse asynchrone d'un résultat, retournée par le constructeur async. Il hérite de Job et ajoute des méthodes : await() pour obtenir le résultat, getCompleted() pour un accès non bloquant et getCompletionExceptionOrNull() pour vérifier les exceptions.
Utilisez le paramètre CoroutineStart.LAZY : scope.launch(start = CoroutineStart.LAZY) { ... }. Appelez ensuite job.start() ou job.join() pour l'exécution réelle. Ceci est utile pour l'initialisation différée et l'exécution conditionnelle de coroutines.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi