52 votes

Considérations sur la conception d'Android : AsyncTask vs Service (IntentService ?)

Je suis en train de concevoir une application Android qui devra effectuer les étapes suivantes :

  1. l'utilisateur appuie sur un bouton ou indique d'une autre manière qu'il veut "synchroniser les données".
  2. Le processus sync utilisera des services web REST pour déplacer les données vers et depuis le serveur.
  3. les données seront stockées localement dans une base de données sqlite.
  4. le processus de synchronisation doit fournir des mises à jour et des messages d'état à l'interface utilisateur.
  5. l'utilisateur ne doit pas être autorisé à s'égarer dans d'autres parties de l'application et à effectuer un travail supplémentaire pendant le processus de synchronisation.

La première fois que le processus de synchronisation s'exécute, cela peut prendre 10 à 20 minutes. Après la synchronisation initiale, moins de données seront transférées et stockées, et le processus de synchronisation prendra de 1 à 2 minutes. Je m'attends à ce que le processus prenne 1 à 2 minutes ou moins.

J'ai lu beaucoup de choses sur le système Android. AsyncTask et divers exemples d'utilisation d'un service ... Mais je ne comprends pas bien les considérations de conception et les compromis à faire pour choisir une conception plutôt qu'une autre. J'ai actuellement mon projet de démonstration qui utilise une AsyncTask. Après avoir regardé (la plupart) Developing Android REST client applications : http://code.google.com/events/io/2010/sessions/developing-RESTful-Android-apps.html# Je suis confus, les modèles de conception décrits ici me semblent exagérés. complexes, peut-être parce que je ne les comprends pas encore.

Je viens de Java, de Spring, d'applications web et de bureau. Penser et concevoir en fonction d'un appareil portable est tout à fait nouveau pour moi. (Que se passe-t-il lorsque la disposition de l'écran est modifiée ? Que se passe-t-il lorsque le téléphone sonne alors que je suis en train d'effectuer une synchronisation ?) En prenant deux pas de recul, si la synchronisation initiale doit être un processus si long, y a-t-il une meilleure façon pour moi de penser au problème->solution, à l'expérience de l'utilisateur, aux attentes de l'utilisateur d'une application fonctionnant sur un téléphone ?

J'aimerais avoir l'avis de développeurs Android plus expérimentés qui se sont déjà penchés sur ces questions.

44voto

Arhimed Points 18116

À mon avis, il s'agit de la partie la plus délicate/difficile d'un développement Android grand public/moyen. Par exemple, sur BlackBerry, c'est BEAUCOUP plus facile.

Vous devez absolument utiliser un Service .

AsyncTask ne convient pas, parce qu'elle est étroitement "liée" à votre Activity via un Context (sinon vous ne pourriez pas mettre à jour l'interface utilisateur de l'application Activity de votre AsyncTask ). Cependant, un Activity peut être tué par l'OS une fois que le Activity est passé à l'arrière-plan. Un exemple de raison de passage en arrière-plan peut être un appel entrant - l'utilisateur bascule vers l'application Téléphone, donc votre Activity devient invisible. Dans ce cas (selon l'état actuel de la RAM), le système d'exploitation peut décider de tuer l'une des activités d'arrière-plan (invisibles pour l'utilisateur).

Certains développeurs contournent ce problème en mettant en place un élément statique à l'intérieur duquel se déroulent des actions de longue durée. Certains recommandent d'utiliser Application instance. C'est parce que les éléments statiques et les Application existent tant que l'ensemble du processus d'application existe. Cependant, il s'agit là de solutions de contournement incorrectes. Les processus d'Android peuvent également être tués lorsque le système d'exploitation décide qu'il est temps de le faire. Le système d'exploitation Android a ses propres considérations sur ce qu'il peut tuer et dans quel ordre. Tous les processus sont divisés en 5 niveaux de "tuabilité". Voici le document où ces niveaux sont spécifiés. Il est intéressant d'y lire :

Parce qu'un processus exécutant un service est est mieux classé qu'un processus avec des activités activités d'arrière-plan, une activité qui initie une opération de longue durée peut faire l'affaire de démarrer un service pour cette opération, plutôt que de simplement créer un thread - surtout si l'opération particulièrement si l'opération est susceptible de durer plus longtemps que l'activité. Exemples exemples sont la lecture de musique en musique en arrière-plan et le téléchargement d'une photo prise par la caméra sur un site web. L'utilisation d'un service garantit que le opération aura au moins la priorité "service processus", indépendamment de ce qui ce qui arrive à l'activité.

Votre Activity où les utilisateurs initient une action de longue durée doit afficher une ProgressDialog pour s'assurer que l'utilisateur ne fait rien d'autre pendant que l'action est en cours. Le guide est aquí .

De plus, vous voudrez probablement utiliser l'option NotificationManager pour notifier à l'utilisateur l'achèvement (ou l'échec) de votre action de longue durée si votre Activity est actuellement invisible. Voici le Info sur le gestionnaire de notifications pour commencer.

12voto

Fuzzical Logic Points 5135

Vous devez prendre en compte de nombreux éléments afin de déterminer la meilleure façon d'aborder votre situation. Il semble que vous ayez besoin d'une bonne comparaison entre les deux approches... Voici donc une liste de similitudes, de différences et de considérations supplémentaires qui doivent être prises en compte lorsque l'on travaille sur un appareil portable.

Un service est une partie de votre application qui n'a pas d'interface utilisateur. Il peut être appelé par une IU (activité) pour être lancé, ou peut être lancé par tout autre composant de votre application. Lors du développement, vous avez la liberté de le placer sur un thread différent, ou même de l'exécuter dans une tâche ou un processus différent. Cela vous permet de le séparer de votre interface utilisateur. En outre, vous pouvez lancer le service pour qu'il s'exécute indépendamment (startService) ou lier votre activité à lui (bindService) en fonction de vos besoins. En utilisant des gestionnaires personnalisés, vous pouvez définir des rappels pour mettre à jour l'interface utilisateur en fonction de votre progression. Un service ne se termine pas nécessairement si un utilisateur change d'activité, mais peut être interrompu à TOUT moment par le système d'exploitation.

Une AsyncTask est toujours instanciée à partir du thread UI. Elle n'autorise que des callbacks spécifiques, mais simplifie le processus de multithreading aux fins de transactions relativement courtes (par rapport à des services threadés séparés dédiés) qui sont intrinsèquement liées aux actions effectuées par une activité. Chaque fois qu'un utilisateur change d'activité, l'AsyncTask est mis en "pause" et peut même mourir car il n'y a plus de thread UI pour votre activité.

Ce qui m'inquiète le plus, c'est que si l'application prend 10 à 20 minutes la première fois, je suppose que l'utilisateur va soit changer de tâche temporairement, soit poser le téléphone jusqu'à ce qu'elle soit terminée (ce qui peut entraîner les mêmes complications si le téléphone dort). Compte tenu de ces considérations, un service threadé lié à votre activité peut être votre meilleur choix. Pour protéger votre interface utilisateur, je créerais un dialogue de progression pour votre activité qui recevrait vos rappels de progression. Cela limite l'entrée de l'utilisateur dans VOTRE application et permet à votre service de continuer à fonctionner comme il le doit. Ensuite, remplacez l'activité onResume pour vérifier le statut de votre service et s'il est en cours d'exécution. Vous pouvez alors réinitialiser le dialogue immédiatement.

Étant donné que c'est ma méthode préférée, je prendrais également en compte le fait que le système d'exploitation peut tuer l'application à tout moment de toute façon. Assurez-vous donc d'avoir un moyen de détecter une synchronisation incomplète ou partielle. Ensuite, vous pouvez reprendre automatiquement lorsque votre activité ou service redémarre.

5voto

chubbsondubs Points 16075

Avec AsyncTask si l'utilisateur passe à une autre activité, vous ne pouvez pas transférer cet objet à l'autre activité et il meurt. Il y a des astuces que vous pouvez jouer lorsque l'utilisateur fait tourner l'écran ou quelque chose comme ça, mais cela ne s'étend pas à la destruction générale. AsyncTask peuvent mourir au hasard.

Google Sync est exécuté en tant que service en arrière-plan car la synchronisation peut prendre un certain temps. Vous devrez peut-être suivre leur chemin et créer votre propre service de synchronisation avec lequel vous pourrez communiquer. Voici quelques idées pour y parvenir :

http://mylifewithandroid.blogspot.com/2008/01/about-binders.html

Il est tout à fait possible de communiquer entre Service et Activité, mais il est difficile de le faire correctement.

3voto

raj Points 311

Le choix dépend principalement de la conception de l'application. Puisque AsyncTask et IntentService ont tous deux leur place, ce que vous attendez de l'application (l'expérience de l'utilisateur) est plus important et vous pouvez choisir l'un ou l'autre ou les deux. Quelques scénarios sont mentionnés ci-dessous (principalement ce que j'ai vécu en développant des applications)

  • Supposons que les applications ont des pages de flux - où plus d'un appel api est fait pour rendre la page présentable ( /getFriends, /getDates, /getPictures etc.) vous pouvez déformer tous ces appels api en un seul AsyncTask avec exécuteur qui est multithread et la séquence d'exécution n'a pas d'importance. Contrairement à IntentService qui exécute tous les appels en séquence dans un seul fil d'exécution. Pour un appareil haut de gamme avec multi-core, l'appel d'AsyncTask est plus efficace. Et si vous lancez l'AsyncTask sur le thread de l'IU, la mise à jour de l'IU est un jeu d'enfant (lire moins de code lourd). Et même si un utilisateur quitte la page, avec une utilisation intelligente de ne pas retenir le contexte, l'application ne se plante pas.

  • En supposant que vous essayez d'écrire une application qui n'a pas besoin que l'utilisateur soit sur la vue/activité/fragment et que le temps d'exécution total pour montrer quelque chose n'est pas critique (supposez un service de synchronisation ou une notification/alarme utilisateur), alors IntentService est un meilleur choix. (il n'est pas nécessaire de lancer une tâche asynchrone sur le fil d'exécution de l'interface utilisateur pour ne pas avoir à écrire un gestionnaire pour forcer les changements sur l'interface utilisateur, etc. etc. et moins de code passe-partout).

D'après mon expérience, écrivez une petite application pour les deux et comparez les avantages et les inconvénients pour vous faire une meilleure idée. (p.s je vous suggère de regarder l'application iosched de google pour avoir une meilleure idée - ils utilisent à la fois Asynctask et IntentService)

2voto

Joe Plante Points 2733

J'ai tendance à préférer le combo IntentService + BroadcastReceiver parce qu'il vous offre un degré de contrôle vraiment élevé.

Vous devez absolument vous assurer que l'interface utilisateur fonctionne si vous mettez à jour quelque chose à l'écran. Les pannes d'ASyncTask ont été signalées comme étant l'une des principales causes de pannes d'Android. Il est possible de les éviter en conservant une sorte de variable "activityIsAlive" et en sautant ou en retardant une mise à jour de l'interface utilisateur si l'activité est morte.

Le combo IntentService + BroadcastReceiver est un peu plus résistant au crash car la plupart des tutoriels vous indiquent de désactiver le BroadcastReceiver onPause ou onStop. Si vous ne le faites pas, vous devrez à nouveau désactiver la mise à jour de l'interface utilisateur. Il y a une commande runOnUiThread quelque part qui vous aidera à faire les mises à jour de l'interface utilisateur.

Le combo IntentService + BroadcastReceiver est également plus extensible. Vous pouvez créer des fonctions et étendre BroadcastReceiver pour créer une solution de traitement REST plus élégante. Cependant, il nécessite plus de plomberie qu'un ASyncTask.

Si vous retardez la mise à jour de l'interface utilisateur, vous pouvez peut-être le faire sur le OnWindowFocusChangedListener. Lorsque cette fonction reçoit true, cela signifie que l'IU est vivante.

tldr ; Assurez-vous que l'activité et/ou le fragment est vivant avant de mettre à jour l'interface utilisateur si vous exécutez quelque chose en arrière-plan.

2015 Edit : vérifiez aussi les Chargeurs. Un peu plus difficile à appréhender car il se passe beaucoup de choses dans les coulisses.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X