web-dev-qa-db-fra.com

Android BroadcastReceiver ou méthode de rappel simple?

Dans mes projets, j'utilise BroadcastReceivers comme rappel à partir d'un long thread d'exécution (par exemple, notifier l'activité qu'un téléchargement a été terminé et envoyer des données de réponse d'un travailleur Thread pour que l'activité puisse afficher le message approprié à l'utilisateur ..). Pour utiliser BroadcastReceivers, je dois faire attention à enregistrer et à désenregistrer le récepteur de diffusion chaque fois que je l'utilise et je dois également faire attention aux messages à envoyer lorsque j'utilise cette méthode pour des actions plus différentes (comme le téléchargement , effectuer des appels WebService, etc.). Et aussi pour envoyer des objets personnalisés via l'intention de Broadcast, je dois également créer les objets Parcelable.

Contrairement à cette approche, j'ai également vu l'approche des méthodes de rappel qui semble être plus simple que la méthode que j'utilise. Les méthodes de rappel sont des implémentations de méthodes d'interface simples qui peuvent être utilisées pour obtenir le même effet que le BroadcastRecaiver dans la messagerie d'application. Cette approche n'a pas besoin de l'implémentation de Parcelable pour renvoyer des objets complexes et elle n'utilise pas de clés comme BroadcastReceiver .. Je pense que la mauvaise partie est que je dois vérifier la valeur nulle de l'objet de rappel avant de vouloir appeler une méthode de rappel .. et aussi pour m'assurer que j'exécute le code de l'implémentation sur le thread d'interface utilisateur afin que je puisse mettre à jour l'interface utilisateur sans erreurs.

D'accord, j'espère que vous avez compris ce que je voulais dire :).

Maintenant, la question est de savoir si la méthode de rappel est meilleure (plus légère, plus propre, plus rapide ..) que l'approche BroadcastReceiver lorsqu'elle est utilisée juste à l'intérieur d'une seule application? (Notez que je n'utilise pas Android Service pour le travail en arrière-plan .. juste AsyncTask et Threads)

Je vous remercie!

55
Cata

C'est une question très intéressante et j'ai rencontré le même problème. À mon avis, les deux mécanismes peuvent être utilisés ensemble et la bonne approche à utiliser dépend de votre cas d'utilisation. Voici quelques points à prendre en compte avant de décider.

L'utilisation du mécanisme de rappel présente certains avantages, mais il existe également des limitations:

PRO

  • Il est simple et direct à mettre en œuvre.
  • Vous bénéficiez d'une sécurité de type entre les composants qui interagissent entre eux.
  • Vous pouvez renvoyer des objets arbitraires.
  • Cela simplifie les tests car vous n'avez qu'à injecter un faux rappel (par exemple généré via mockito ou quelque chose de similaire) dans les tests unitaires.

CONTRA

  • Vous devez basculer vers le thread principal afin de faire des manipulations d'interface utilisateur.
  • Vous ne pouvez avoir qu'une relation 1 à 1. Une relation 1 à n (modèle d'observateur) n'est pas réalisable sans travaux supplémentaires. Dans ce cas, je préférerais le mécanisme Observer/Observable d'Android.
  • Comme vous l'avez déjà dit, vous devez toujours vérifier null avant d'appeler des fonctions de rappel si le rappel peut être facultatif.
  • Si votre composant doit offrir une sorte d'API de service avec différentes fonctions de service et que vous ne voulez pas avoir une interface de rappel avec seulement quelques fonctions de rappel génériques, vous devez décider si vous fournissez une interface de rappel spéciale pour chaque fonction de service ou si vous fournir une interface de rappel unique avec de nombreuses fonctions de rappel. Dans le dernier cas, tous les clients de rappel utilisés pour les appels de service à votre API doivent implémenter l'interface de rappel complète bien que la majorité des corps de méthode soient vides. Vous pouvez contourner cela en implémentant un stub avec des corps vides et faire en sorte que votre client de rappel hérite de ce stub, mais cela n'est pas possible s'il hérite déjà d'une autre classe de base. Vous pouvez peut-être utiliser une sorte de rappel de proxy dynamique (voir http://developer.Android.com/reference/Java/lang/reflect/Proxy.html ), mais cela devient vraiment complexe et je penserait à utiliser un autre mécanisme.
  • Le client pour les appels de rappel doit être propagé à travers différentes méthodes/composants s'il n'est pas directement accessible par l'appelant du service.

Quelques points concernant l'approche BroadcastReceiver-:

PRO

  • Vous obtenez un couplage lâche entre vos composants.
  • Vous pouvez avoir une relation de 1 à n (y compris de 1 à 0).
  • La méthode onReceive() est toujours exécutée sur le thread principal.
  • Vous pouvez notifier les composants dans l'ensemble de votre application, afin que les composants communicants n'aient pas à se "voir".

CONTRA

  • Il s'agit d'une approche très générique, donc le marshaling et le dé-marshalling des données transportées par le Intent est une source d'erreur supplémentaire.
  • Vous devez rendre les actions de votre Intent uniques (par exemple en ajoutant le nom du package) si vous souhaitez éliminer les corrélations avec d'autres applications, car leur objectif initial est de faire des diffusions entre les applications.
  • Vous devez gérer l'enregistrement et la désinscription de BroadcastReceiver-. Si vous souhaitez le faire de manière plus confortable, vous pouvez implémenter une annotation personnalisée pour annoter votre activité avec les actions qui doivent être enregistrées et implémenter une base Activityclasse qui effectue l'enregistrement et la désinscription avec IntentFilters dans sa onResume() resp. onPause() méthodes.
  • Comme vous l'avez déjà dit, les données envoyées avec Intent doivent implémenter l'interface Parcelable, mais en outre, il y a une limitation de taille stricte et cela entraînera des problèmes de performances si vous transportez une grande quantité de données avec votre Intent. Voir http://code.google.com/p/Android/issues/detail?id=5878 pour une discussion à ce sujet. Donc, si vous voulez envoyer des images par exemple, vous devez les stocker temporairement dans un référentiel et envoyer un ID ou une URL correspondante pour accéder à l'image depuis le récepteur de votre Intent qui la supprime du référentiel après utilisation. Cela entraîne d'autres problèmes s'il y a plusieurs récepteurs (quand l'image doit-elle être supprimée du référentiel et qui doit le faire?).
  • Si vous abusez de ce type de mécanisme de notification, le flux de contrôle de votre application peut être masqué et lors du débogage, vous finissez par dessiner des graphiques avec des séquences de Intents pour comprendre ce qui a déclenché une erreur spécifique ou pourquoi cette chaîne de notification est interrompue à quelque point.

À mon avis, même une application mobile devrait avoir une architecture de base sur au moins 2 couches: la couche UI et la couche principale (avec la logique métier, etc.). En général, les tâches longues sont exécutées dans un propre thread (peut-être via AsyncTask ou HandlerThread si vous utilisez MessageQueues) à l'intérieur de la couche principale et l'interface utilisateur doit être mise à jour une fois cette tâche a été terminé. En général, avec les rappels, vous réalisez un couplage étroit entre vos composants, donc je préférerais utiliser cette approche uniquement au sein d'une couche et non pour la communication à travers les limites de la couche. Pour la diffusion de messages entre l'interface utilisateur et la couche principale, j'utiliserais l'approche BroadcastReceiver- qui vous permet de découpler votre couche interface utilisateur de la couche logique.

82
Andreas Klöber

Je ne vois pas ce que vous gagnez en utilisant BroadcastReceiver dans votre cas. Les rappels ou, mieux encore, Handlers seraient le moyen de le faire. BroadcastReceiver est bon lorsque vous ne savez pas qui sont les abonnés.

6

Je vais simplement ajouter une autre option aux autres bonnes réponses que vous avez déjà reçues ...

Vous n'avez pas besoin de créer un récepteur de diffusion pour recevoir des intentions. Dans votre fichier manifeste Android Android, vous pouvez enregistrer n'importe quelle activité pour recevoir des intentions:

<activity Android:name=".MyActivity">
        <intent-filter >
              <action Android:name="intent.you.want.to.receive" /> 
              <category Android:name="Android.intent.category.DEFAULT" /> 
        </intent-filter>     
....
</activity>

Remplacez ensuite la méthode onNewIntent(Intent) dans votre activité pour la recevoir.

Pour envoyer l'intention, utilisez la méthode Context.startActivity(Intent). Vous souhaiterez probablement ajouter l'indicateur FLAG_ACTIVITY_SINGLE_TOP À votre intention afin qu'il ne crée pas une nouvelle instance de votre activité si une est déjà en cours d'exécution.

EDIT: Je viens de remarquer que vous utilisez une seule application. Par conséquent, un simple rappel est probablement préférable. La solution ci-dessus fonctionne dans une seule application, mais est plus appropriée pour différentes applications. Je vais laisser ça ici juste au cas où ça aiderait quelqu'un. Bonne chance!

3
objbuilder

Les diffuseurs doivent être utilisés Si vous devez envoyer des diffusions à travers les applications, alors que les rappels (ou les gestionnaires comme suggéré par Alex) sont mieux à utiliser dans votre situation.

Si vous souhaitez utiliser autre que ces deux, envisagez d'utiliser Observer (Interface incluse dans Android) et déléguez.

Pour les délégués, veuillez considérer this SO post .

2
silwar

je ne sais pas quel est l'objectif, mais si vous souhaitez conserver la même idée d'utiliser intention et broadcastReceiver, et souhaitez de meilleures performances et sécurité que les broadcastReceivers normaux, vous pouvez essayer cette démo, disponible dans le support Android bibliothèque :

http://developer.Android.com/resources/samples/Support4Demos/src/com/example/Android/supportv4/content/LocalServiceBroadcaster.html

sinon, vous pouvez toujours utiliser asyncTask, service, handlers, etc ...

1