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:
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.À 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.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 ViewModel
LiveData
. 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.)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é:
Service
et Activity
?Channel
ou Flow
?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) .