Programmation réactive en Kotlin : la puissance de kotlinx.coroutines et Flow
\n\nLa programmation réactive est un paradigme orienté vers le traitement asynchrone des flux de données et la propagation des changements. Dans l'écosystème Kotlin, l'outil principal pour implémenter des modèles réactifs est la bibliothèque kotlinx.coroutines, et plus précisément son composant Flow. Contrairement à RxJava, Flow est étroitement intégré aux coroutines, rendant le code plus concis et plus sûr.
Concepts fondamentaux de Flow
\n\nFlow est un flux de données asynchrone froid qui émet des valeurs séquentiellement. Il prend en charge :
\n- \n
- Les flux froids — les données ne commencent à être générées qu'en présence d'un abonné (opérateur terminal). \n
- Le contexte d'exécution — le flux peut basculer entre différents répartiteurs de coroutines. \n
- L'annulation via les coroutines — l'annulation de la coroutine parente termine automatiquement le Flow. \n
Installation et configuration
\nPour utiliser Flow, ajoutez la dépendance dans build.gradle.kts :
dependencies {\n implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")\n}\n\nCréation et consommation d'un Flow
\nCréons un flux simple de nombres avec un délai :
\nimport kotlinx.coroutines.*\nimport kotlinx.coroutines.flow.*\n\nfun simpleFlow(): Flow<Int> = flow {\n for (i in 1..5) {\n delay(100) // simulation d'un travail asynchrone\n emit(i) // émet la valeur\n }\n}\n\nfun main() = runBlocking {\n simpleFlow().collect { value ->\n println("Reçu : $value")\n }\n}\nDans cet exemple, flow { ... } crée un flux froid, et collect est l'opérateur terminal qui déclenche l'exécution.
Transformation des données avec des opérateurs
\nFlow fournit un riche ensemble d'opérateurs, similaires à RxJava :
\n- \n
map— transforme chaque valeur. \n filter— filtre les valeurs selon une condition. \n flatMapConcat— concatène séquentiellement les flux. \n catch— gère les exceptions à l'intérieur du flux. \n
Exemple avec des opérateurs :
\nfun transformFlow(): Flow<String> = flow {\n emit("Kotlin")\n emit("Java")\n emit("Python")\n}.map { it.lowercase() }\n .filter { it.startsWith("k") }\n\nfun main() = runBlocking {\n transformFlow().collect { println(it) }\n // Sortie : kotlin\n}\n\nGestion des erreurs et de la fin
\nFlow permet de gérer élégamment les exceptions avec catch et d'exécuter des actions de fin via onCompletion :
fun errorProneFlow(): Flow<Int> = flow {\n emit(1)\n emit(2)\n throw RuntimeException("Erreur !")\n emit(3) // ne sera pas exécuté\n}\n\nfun main() = runBlocking {\n errorProneFlow()\n .catch { e -> println("Erreur attrapée : ${e.message}") }\n .onCompletion { println("Flux terminé") }\n .collect { println(it) }\n}\n// Sortie : 1, 2, Erreur attrapée : Erreur !, Flux terminé\n\nFlux chauds : StateFlow et SharedFlow
\nContrairement au Flow froid, StateFlow et SharedFlow sont des flux chauds qui conservent un état et peuvent avoir plusieurs abonnés.
\n\nStateFlow
\nUtilisé pour stocker et observer un état (par exemple, dans un ViewModel) :
\nclass CounterViewModel {\n private val _counter = MutableStateFlow(0)\n val counter: StateFlow<Int> = _counter.asStateFlow()\n\n fun increment() {\n _counter.value++\n }\n}\n\n// Dans une Activity ou un Fragment\nlifecycleScope.launch {\n viewModel.counter.collect { value ->\n textView.text = "Compteur : $value"\n }\n}\n\nSharedFlow
\nAdapté pour
Aussi dans la bibliothèque
Génération de rapports PDF en Go : aperçu des bibliothèques et exemples de code
godotenv Go : guide complet avec exemples pratiques
Sérialisation d'objets en Swift (iOS/macOS) : Codable, JSON et PropertyList
Travailler avec les archives ZIP en PHP : guide complet sur ZipArchive
Paging 3 pour Kotlin : guide complet avec exemples