Si vous vous intéressez au développement Android, vous avez certainement déjà rencontré le dilemme récurrent de la gestion des données dans la couche présentation. Longtemps, LiveData a dominé le marché, mais l'arrivée des coroutines Kotlin a permis l'émergence d'outils bien plus puissants et flexibles. On pense notamment à StateFlow et SharedFlow , qui représentent l'évolution naturelle pour une gestion réactive et efficace des flux de données.
En réalité, bien que ces concepts puissent paraître similaires au premier abord, chacun a sa propre utilité. Souhaiter que l'écran conserve en mémoire l'endroit où le flux d'actualités s'est arrêté est différent de vouloir envoyer une notification rapide pour un Toast qui ne doit être consulté qu'une seule fois. Pour éviter toute confusion, analysons le fonctionnement de ces flux et pourquoi il est préférable d'abandonner LiveData dans vos projets modernes.
Le concept de flux : froid ou chaud ?
Pour comprendre StateFlow et SharedFlow, il faut d'abord saisir la différence entre un flux froid et un flux chaud. Un flux standard est froid ; il est comparable à une recette : rien ne se passe tant que personne ne décide de l'exécuter (en appelant `collect`). Le problème est que si vous avez trois abonnés, le code du producteur s'exécute trois fois, ce qui peut épuiser la batterie ou générer des requêtes API redondantes.
C’est là qu’interviennent les flux actifs. StateFlow et SharedFlow sont tous deux des flux actifs de manière stratégique : le producteur reste actif même si personne n’écoute. Cela permet à plusieurs collecteurs de partager la même source de données , évitant ainsi à l’application d’effectuer plusieurs fois la même tâche, ce qui est essentiel pour maintenir la fluidité du système.
StateFlow : le remplaçant idéal de LiveData
StateFlow est essentiellement un conteneur d'état. Sa principale caractéristique est de toujours conserver une valeur actuelle , accessible de manière synchrone via la propriété `.value`. C'est l'outil idéal pour gérer l'état de l'interface utilisateur, par exemple pour indiquer si un écran est en cours de chargement ou si les données de profil ont été mises à jour avec succès.
Contrairement à LiveData, StateFlow exige une valeur initiale dans son constructeur, éliminant ainsi l'incertitude liée aux états nuls au démarrage. De plus, il implémente un comportement de fusion et de déduplication : si vous émettez la même valeur deux fois de suite, le flux l'ignore simplement. Ceci est très utile pour éviter que l'interface utilisateur ne se rafraîchisse inutilement lorsque les données n'ont pas changé.
Pour implémenter cela dans un ViewModel, la meilleure approche consiste à utiliser une propriété de repli MutableStateFlow privée et à l'exposer comme un StateFlow immuable. Cela garantit que seul le ViewModel peut modifier l'état, respectant ainsi le flux de données unidirectionnel et empêchant la vue de manipuler les données.
SharedFlow : Le bus événementiel pour des actions uniques
Si StateFlow gère l'état, SharedFlow gère les événements. Imaginez que vous souhaitiez afficher une boîte de dialogue d'erreur ou naviguer vers un autre écran. Avec StateFlow, la rotation de l'appareil entraînerait la réception de la dernière valeur par le nouveau collecteur, et l'événement se déclencherait à nouveau — un bug critique. SharedFlow résout ce problème grâce à sa capacité à être configuré avec une valeur de relecture nulle.
Ce flux est beaucoup plus flexible et nous permet d'ajuster des paramètres tels que la taille du tampon de débordement . Si ce dernier est plein, nous pouvons choisir de suspendre l'émetteur ou de supprimer la valeur la plus ancienne ou la plus récente. Un avantage majeur est que SharedFlow permet d'envoyer des valeurs répétées ; si vous devez envoyer le même message « Erreur de connexion » trois fois, SharedFlow le fera sans hésiter, tandis que StateFlow les filtrerait.
Comparaison technique : StateFlow vs SharedFlow vs LiveData
Pour éviter toute confusion lors d'un entretien technique ou de la conception de votre architecture, il est important de prendre en compte les points essentiels. LiveData est simple et gère automatiquement le cycle de vie , mais il est fortement dépendant d'Android. StateFlow et SharedFlow, faisant partie des coroutines Kotlin, sont indépendants de la plateforme , ce qui simplifie considérablement les tests unitaires et la création de couches métier claires.
- Flux d'état: Force une valeur initiale, possède une propriété .value, déduplique les valeurs et est idéal pour les états d'interface utilisateur.
- Flux partagéElle ne nécessite pas de valeur initiale, est configurable en termes de relecture et constitue l'option idéale pour les événements éphémères.
- Données en direct: Elle ne nécessite pas de valeur initiale, elle est consciente du cycle de vie mais est moins performante dans les transformations asynchrones.
L'importance d'une récolte sûre
Une erreur fréquente consiste à exécuter une méthode `collect` dans une coroutine simple au sein de l'activité. Cette pratique est dangereuse car le traitement des données se poursuit même lorsque l'application est en arrière-plan , ce qui peut entraîner des plantages ou une utilisation excessive du processeur. La solution moderne consiste à utiliser l' API `repeatOnLifecycle(Lifecycle.State.STARTED)`.
Cette fonction active la collecte lorsque la vue est visible et l'annule automatiquement lorsqu'elle atteint l'état ARRÊTÉ . Pour Jetpack Compose, il est recommandé d'utiliser `collectAsStateWithLifecycle()` , qui intègre cette logique nativement et garantit une utilisation efficace des ressources de l'appareil.
Transformer les flux froids en flux chauds
Il arrive parfois qu'un flux provenant d'une base de données (comme Room) ou d'un dépôt soit inactif. Pour l'activer, on utilise les opérateurs `stateIn` ou `shareIn` . Ces opérateurs requièrent un `CoroutineScope` et une stratégie de démarrage. La plus courante est `SharingStarted.WhileSubscribed(5000)`.
Cette configuration est ingénieuse car elle maintient le flux actif tant qu'il y a des abonnés, et si l'écran est fermé, elle attend 5 secondes avant de l'arrêter. Ceci est essentiel pour que, lors d'une rotation d'écran , le flux ne soit pas interrompu et recréé, évitant ainsi des requêtes inutiles au serveur ou à la base de données locale.
La maîtrise de ces outils implique de comprendre que l'état doit être persistant et dédupliqué grâce à StateFlow, tandis que les actions ponctuelles doivent transiter par SharedFlow. En combinant cela avec la prise en compte du cycle de vie et la transformation des flux à froid via stateIn, nous obtenons une architecture Android robuste et entièrement réactive, optimisée pour des performances optimales sur les appareils mobiles. Partagez ce guide pour aider d'autres personnes à découvrir ce sujet.
