Coroutines — concepts clés, Job et Dispatchers en Kotlin

Auteur : IT Sectr Publié le : 2026-03-16 Temps de lecture : 8 min

Les coroutines (Coroutines) sont des threads légers en Kotlin pour la programmation asynchrone, disponibles via la bibliothèque kotlinx.coroutines. Selon JetBrains Kotlin Documentation, 2026, Coroutines permettent de suspendre l'exécution d'une fonction sans bloquer un thread, contrairement aux Threads traditionnels. Les coroutines s'exécutent sur un pool de threads limité, ce qui les rend mille fois plus légères que les threads natives. Kotlin Coroutines sont entièrement intégrées à Android Jetpack, Retrofit, Room et d'autres bibliothèques populaires de l'écosystème Android.

Points Clés

  • Coroutines — threads légers Kotlin pour du code asynchrone sans blocage
  • Fonction suspend — une fonction qui peut se mettre en pause et reprendre sans bloquer un thread
  • Dispatcher détermine le pool de threads pour l'exécution de la coroutine
  • Job — un descripteur de coroutine avec prise en charge d'annulation et suivi d'état
  • CoroutineScope gère le cycle de vie des coroutines et leur annulation à la fin

Que sont les coroutines Kotlin

Coroutines sont un mécanisme de programmation asynchrone en Kotlin, implémenté dans la bibliothèque kotlinx.coroutines. Contrairement aux threads du système d'exploitation, les coroutines ne sont pas liées à un thread spécifique : elles peuvent se suspendre sur un thread et reprendre sur un autre. Un seul thread peut exécuter des milliers de coroutines, en basculant entre elles avec une surcharge minimale.

Les coroutines sont apparues dans Kotlin 1.3 (2018) en tant que fonctionnalité expérimentale et sont devenues stables dans Kotlin 1.5 (2021). Coroutines résolvent le problème de l'enfer des callbacks de manière similaire à async/await, mais offrent une API plus riche : canaux (Channel), Flow, gestion des exceptions dans la hiérarchie Job et intégration directe avec Android Lifecycle.

Selon JetBrains (2025), chaque coroutine consomme environ 100 octets de mémoire contre 1+ Mo pour un thread natif. Cela permet d'exécuter des millions de coroutines dans une seule application sans risque d'OutOfMemoryError. C'est la légèreté des coroutines qui en fait l'outil préféré pour l'asynchronisme dans Android.

Comment les coroutines fonctionnent sous le capot

Chaque coroutine Kotlin est compilée en une machine d'état via le Continuation Passing Style (CPS). Le compilateur ajoute un paramètre Continuation caché à chaque fonction suspend. Le Continuation contient le point de reprise et toutes les variables locales. Lorsqu'une coroutine se suspend, le runtime sauvegarde le Continuation, et lorsqu'elle reprend, il le restaure sur n'importe quel thread disponible du pool du Dispatcher.

Fonctions suspend : pause et reprise

suspend est un mot-clé Kotlin qui marque une fonction comme suspensible. Une telle fonction ne peut être appelée que depuis une autre fonction suspend ou depuis une coroutine. À l'intérieur d'une fonction suspend, vous pouvez appeler d'autres fonctions suspend dans n'importe quel ordre, et chaque point d'appel est un point de suspension potentiel.

La mécanique est simple : lorsqu'une fonction suspend appelle une autre fonction suspend, elle se suspend à ce point, libérant le thread. Une fois la fonction appelée terminée, le runtime continue l'exécution à partir de l'emplacement sauvegardé. C'est ce qu'on appelle l'annulation coopérative (cooperative cancellation) — aucun thread n'est bloqué.

  • Suspension — la coroutine libère le thread sans le bloquer
  • Reprise — la coroutine reprend là où elle s'est suspendue
  • Thread — une coroutine peut se suspendre sur le thread A et reprendre sur le thread B
  • Exceptions — traitées via try/catch comme dans du code synchrone

Important : une fonction suspend n'est pas asynchrone par défaut. L'ordre d'exécution reste séquentiel si launch ou async ne sont pas utilisés. suspend permet simplement à la fonction d'être mise en pause sans bloquer le thread et de faire partie du contexte de coroutine. Continuation Passing Style est un modèle de compilation où chaque fonction suspend reçoit un callback Continuation caché, et le compilateur génère une machine d'état pour gérer les suspensions et les reprises.

CoroutineScope et concurrence structurée

CoroutineScope est un contexte qui définit le cycle de vie des coroutines. Toutes les coroutines doivent être lancées dans un scope. Lorsqu'un scope est annulé (par exemple, à la fin d'une Activity), toutes ses coroutines filles sont automatiquement annulées. Cela évite les fuites de tâches en arrière-plan. Android Jetpack fournit des scopes prêts à l'emploi pour chaque composant : viewModelScope pour ViewModel et lifecycleScope pour Activity et Fragment, qui sont automatiquement annulés lorsque le composant correspondant est détruit.

La concurrence structurée (Structured Concurrency) est un principe qui garantit qu'une coroutine ne se termine pas avant que toutes ses coroutines filles ne soient terminées. La hiérarchie Job forme un arbre : une coroutine racine crée un job parent, les enfants créent des jobs enfants. L'annulation d'un job parent se propage à tous les enfants. Structured Concurrency est une différence fondamentale entre les coroutines et les threads.

ScopeOù il est utiliséAnnulation
GlobalScopeUniquement pour les tâches démonsN'est pas annulé automatiquement
viewModelScopeAndroid ViewModelLors du nettoyage du ViewModel
lifecycleScopeAndroid Activity/FragmentLors de la destruction du lifecycle
coroutineScopeDans une fonction suspendLors de l'annulation du job parent

SupervisorJob pour la gestion des erreurs

Un Job normal annule tous les siblings lorsqu'une coroutine fille échoue. SupervisorJob est une exception : un échec dans une coroutine fille n'affecte pas les autres. Ceci est important lorsque plusieurs tâches indépendantes sont exécutées en parallèle et que l'une d'elles peut échouer sans avoir besoin d'annuler les autres.

Dispatchers et constructeurs de coroutines

Dispatchers déterminent sur quels threads les coroutines sont exécutées. Dispatchers.Main — le thread principal de l'UI Android. Dispatchers.IO — un pool pour les opérations bloquantes (réseau, disque). Dispatchers.Default — pour les tâches intensives en CPU. Dispatchers.Unconfined — démarre dans le thread actuel mais ne garantit pas d'y rester. Choisir le bon Dispatcher est crucial pour les performances : une tâche IO sur Default bloquera le pool de calcul, tandis qu'une tâche CPU sur IO créera des threads inutiles.

withContext — une fonction pour changer de Dispatcher à l'intérieur d'une coroutine. Par exemple, une fonction suspend qui analyse du JSON peut basculer sur Dispatchers.Default pour le calcul et revenir sur Dispatchers.Main pour mettre à jour l'UI. withContext est le constructeur le plus utilisé dans le développement Android.

Trois principaux constructeurs de coroutines

launch — lance une coroutine, retourne un Job, ne retourne pas de résultat (fire-and-forget). async — lance une coroutine, retourne un Deferred dont le résultat peut être obtenu via await. runBlocking — bloque le thread actuel pour exécuter une coroutine (uniquement pour les tests et les fonctions main). Choix du constructeur dépend du scénario : launch convient pour les événements et les mises à jour, async pour les tâches avec résultat, runBlocking uniquement pour les tests ou les points d'entrée.

Exemples de code avec coroutines en Kotlin

Considérons trois scénarios pratiques : une coroutine de base avec launch, un appel parallèle avec async et la gestion des erreurs avec SupervisorJob.

Lancer une coroutine avec launch

viewModelScope.launch lance une coroutine dans le contexte du ViewModel. Lorsque le ViewModel est nettoyé, la coroutine est automatiquement annulée.

kotlin
class ProfileViewModel : ViewModel() {
    fun loadUser() {
        viewModelScope.launch(Dispatchers.IO) {
            val user = api.fetchUser()
            withContext(Dispatchers.Main) {
                showUser(user)
            }
        }
    }
}

Requêtes parallèles avec async

coroutineScope avec async lance trois requêtes en parallèle. Les résultats sont collectés via .await(). Si une requête échoue, toutes sont annulées.

kotlin
suspend fun loadDashboard(): Dashboard = coroutineScope {
    val user = async { api.fetchUser() }
    val posts = async { api.fetchPosts() }
    val stats = async { api.fetchStats() }
    Dashboard(user.await(), posts.await(), stats.await())
}

Gestion des erreurs avec SupervisorJob

SupervisorJob permet à chaque coroutine de se terminer indépendamment. Une erreur dans une requête n'annule pas les autres.

kotlin
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
    try { api.fetchUsers() } catch (e: Exception) { log(e) }
}
scope.launch {
    try { api.fetchPosts() } catch (e: Exception) { log(e) }
}

Coroutines vs threads : comparaison et scénarios

Les threads sont une primitive du système d'exploitation. Chaque thread a sa propre pile (~1 Mo) et nécessite un appel système pour la création et le changement. Les coroutines sont une primitive du langage, non liée à l'OS. Elles utilisent Continuation pour sauvegarder l'état et basculent au niveau du runtime sans appels système.

  • Mémoire — thread ~1 Mo, coroutine ~100 octets. Un rapport de 10 000
  • Création — thread ~1 µs d'appel système, coroutine ~0,01 µs au niveau JVM
  • Basculement — thread ~0,1 µs (appel système), coroutine ~0,001 µs (continuation)
  • Maximum — des milliers de threads vs des millions de coroutines par appareil
  • Annulation — un thread ne peut pas être annulé de l'extérieur (Thread.stop obsolète), une coroutine peut l'être via Job.cancel()

Selon Google (2025), l'utilisation de coroutines au lieu de threads réduit la consommation mémoire pour les tâches d'arrière-plan dans les applications Android de 90 à 95 %. Toutes les bibliothèques Android modernes (Retrofit, Room, WorkManager) ont un support intégré des coroutines via les fonctions suspend. Ktor (framework client HTTP de JetBrains) est également entièrement construit sur les coroutines, fournissant des fonctions suspend pour chaque requête sans API de callbacks. Room prend en charge les coroutines via des fonctions suspend dans le DAO, permettant d'exécuter des requêtes de base de données sans bloquer le thread principal.

Quand utiliser les threads au lieu des coroutines

Les threads restent nécessaires pour le code natif via JNI, les appels bloquants CPU-intensive sans limite de temps (rendu vidéo, simulations) et lors de l'intégration avec des bibliothèques C. Pour tout le reste — des coroutines.

Questions Fréquentes

En quoi une coroutine diffère-t-elle d'un thread ?

Coroutine est une unité de travail suspensible qui s'exécute sur un thread existant. Un thread est une ressource système avec sa propre pile. Les coroutines sont des milliers de fois plus légères que les threads et ne bloquent pas les ressources lors de la suspension.

Qu'est-ce que Dispatchers.IO et en quoi diffère-t-il de Default ?

Dispatchers.IO est conçu pour les opérations d'E/S bloquantes (réseau, fichiers) et peut créer de nouveaux threads si nécessaire. Dispatchers.Default a un pool de taille fixe (nombre de cœurs CPU) pour les calculs intensifs en CPU.

Comment annuler une coroutine en cours d'exécution ?

Job.cancel() annule la coroutine et tous ses enfants. Pour vérifier l'annulation à l'intérieur d'une coroutine, utilisez ensureActive() — il lève une CancellationException si la coroutine est annulée.

Les coroutines peuvent-elles être utilisées avec RxJava ?

Oui — via la bibliothèque kotlinx-coroutines-rx3. Elle fournit les fonctions awaitSingle, awaitFirst et autres pour convertir Observable/Single en fonctions suspend et inversement via flowable.

Qu'est-ce que Flow dans les coroutines ?

Flow est un flux de données asynchrone à froid, l'équivalent dans les coroutines de RxJava Observable. Flow émet des valeurs séquentiellement et se termine par une exception ou un succès. Il prend en charge map, filter, catch et d'autres opérateurs.

Résumé

  • Coroutines — threads légers Kotlin avec suspension non bloquante via Continuation Passing Style
  • suspend — le mot-clé pour marquer les fonctions suspensibles
  • Dispatchers gèrent le pool de threads : Main, IO, Default respectivement
  • CoroutineScope lie le cycle de vie des coroutines à un composant (Activity, ViewModel)
  • launch lance une coroutine sans résultat, async/await — avec résultat
  • Structured Concurrency garantit l'annulation hiérarchique des coroutines filles
  • Coroutines vs threads — les coroutines sont 10 000 fois plus légères et sont la norme pour Android

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.

Discuter du projet

Lisez aussi