web-dev-qa-db-fra.com

Utilisation de Kotlin Coroutines pour remplacer LocalBroadcastManager pour la messagerie Firebase

Lorsque vous utilisez Firebase Cloud Messaging sur Android, il est souvent souhaitable de notifier le Activity actuel d'une notification Push entrante. L'une des façons recommandées de le faire a été d'utiliser LocalBroadcastManager pour envoyer un Intent de l'implémentation FirebaseMessagingService à Activity ( exemple de réponse StackOverflow ).

Cependant, depuis la version 1.1.0-alpha01 (2018-12-17), LocalBroadcastManager est déconseillé :

LocalBroadcastManager est un bus d'événements à l'échelle de l'application et englobe les violations de couche dans votre application: n'importe quel composant peut écouter les événements de n'importe quel autre. Vous pouvez remplacer l'utilisation de LocalBroadcastManager par une autre implémentation de modèle observable, en fonction de votre cas d'utilisation, les options appropriées peuvent être LiveData ou des flux réactifs.

Bien qu'il soit très probable que cette classe reste disponible pendant un certain temps, je voudrais quand même commencer à nettoyer nos applications, donc je veux migrer vers quelque chose de mieux avant que Google ne supprime réellement l'ancienne méthode.

À l'heure actuelle, ces émissions locales ont deux rôles principaux dans nos applications:

  1. Mettez à jour l'interface utilisateur avec les nouvelles données de la notification Push. La façon dont cela a fonctionné était que chaque Activity qui se soucie des données Push entrantes a un récepteur de diffusion qui écoute le message approprié et met à jour ses propres données d'affichage.
  2. Forcer l'utilisateur à se déconnecter si le serveur envoie une notification pour mettre fin à la session. Cela fonctionne avec chaque activité ayant une instance d'un récepteur de diffusion qui écoute un événement de déconnexion, termine l'activité et démarre l'activité de connexion.

À mon avis, ces cas d'utilisation ont des problèmes avec les deux alternatives proposées:

  • LiveData est plus facile à utiliser dans un Activity ou Fragment dans le cadre d'un ViewModel. Cependant, ViewModel est uniquement destiné à être utilisé à partir des classes qui traitent directement avec l'interface utilisateur. Accéder au ViewModel à partir du FirebaseMessagingService prend un vilain hack et est une très mauvaise idée d'un point de vue architectural. De plus, différentes activités et fragments ont différents objets ViewModel, et je ne veux pas que le service ait besoin de tous y accéder.
  • Je peux créer un Kotlin object (alias Singleton) avec un tas de propriétés LiveData, demander à FirebaseMessagingService de mettre à jour ces objets LiveData à partir des messages entrants, et demandez à Activity d'observer ces modifications et de les copier dans ses propres propriétés ViewModelLiveData. Le problème est double: tout d'abord, il me faut avoir deux objets LiveData identiques pour chaque élément de données, un dans le ViewModel et un dans le object; et deuxièmement, cela ne m'aide pas à gérer "l'événement de déconnexion", car LiveData est destiné à gérer les données changeantes, pas à écouter un flux d'événements. (Je peux peut-être gérer le deuxième problème en utilisant ce LiveData Event Wrapper , mais cela ressemble toujours à un mauvais piratage sur quelque chose qui n'est pas censé fonctionner de cette façon.)
  • Alors que les flux réactifs, tels que RxJava, feront probablement ce dont j'ai besoin, j'ai déjà forcé mon équipe à apprendre Kotlin, Android Databinding, Android ViewModel, et un beaucoup d'autres nouveautés au cours des derniers mois, et je ne pense pas qu'elles puissent en prendre beaucoup plus. afin de justifier son ajout.

Une suggestion que j'ai trouvée était d'utiliser Kotlin Coroutines avec Channel s ou Flow s. Ceux-ci peuvent être utilisés de manière très similaire aux flux réactifs, mais (contrairement à RxJava) sont destinés à être utilisés avec Kotlin et bénéficient des améliorations de Kotlin par rapport à Java. Cette option est particulièrement intéressante maintenant que Google a annoncé qu'elle se concentre sur Kotlin pour le développement Android au lieu de Java).

Bien que cela me semble être la meilleure option, je n'ai pas réussi à trouver de commentaires des autres sur le fait que cela fonctionne et s'il y a des effets secondaires et/ou des pièges à une telle implémentation. La seule chose que j'ai trouvée était un problème ouvert sur le kotlinx.coroutines référentiel sur la nécessité de fournir un exemple d'une application comme celle-ci. Bien que j'aimerais contribuer à un tel exemple, je ne pense pas en savoir suffisamment pour en créer un bon exemple, et je ne veux pas que mes applications de production soient le cobaye. Je ne sais pas non plus s'il est préférable (ou approprié) d'utiliser des cours explicites avec Channel ou d'utiliser suspend avec Flow dans ce cas.

En résumé:

  • Est-ce que Kotlin Coroutines et leurs structures de concurrence associées sont un bon moyen de gérer la communication entre Android Service et Activity?
  • Si oui, quel type de Kotlin a plus de sens à utiliser, Channel ou Flow?
10
Moshe Katz

Les coroutines n'aident pas vraiment au transfert de données d'un composant logiciel à un autre. Ils aident au traitement de plusieurs unités de travail asynchrone à l'aide d'une syntaxe qui apparaît comme si elles étaient synchrones. C'est la ligne de fond pour les coroutines. Ils sont analogues à la syntaxe async/wait en JavaScript. Bien que vous puissiez utiliser une coroutine pour accéder aux données à partir de sources asynchrones, elle ne vous donne pas de primitves pour proxy ces données sur d'autres composants.

LiveData fonctionne probablement très bien pour ce que vous essayez de faire. Ne confondez pas ViewModel avec LiveData - ils résolvent différents problèmes. Bien que vous ayez raison de dire que ViewModel ne doit être accessible que par du code qui traite de l'interface utilisateur, cette directive ne s'étend pas à LiveData. Il est parfaitement raisonnable d'exposer une LiveData qui reflète les données actuelles de FirebaseMessagingService qui sont ensuite récupérées par un ViewModel, transformées et transmises à une vue. Ce LiveData peut être un singleton, ou obtenu via l'infrastructure d'injection de dépendances que vous choisissez.

Gardez à l'esprit que LiveData n'est vraiment censé être utilisé que pour gérer les changements d'état. Ce n'est pas un "flux" de données que votre application peut écouter. Vous devrez vous assurer que votre infrastructure est basée sur l'état pour que cela fonctionne bien. FCM lui-même n'est pas basé sur un état, mais si vous voulez que vos vues répondent aux messages de FCM, vous devrez conserver suffisamment de contexte entre chaque message pour vous assurer que votre interface utilisateur répond de manière cohérente aux nouveaux messages (ou à l'absence totale de messages) .

2
Doug Stevenson