261 votes

AsyncTask est vraiment conceptuellement erronée ou suis-je juste raté quelque chose ?

J'ai étudié ce problème depuis des mois maintenant, est venu avec de nouvelles solutions, dont je ne suis pas heureux, car ils sont tous les massifs hacks. Je ne peux toujours pas croire qu'une classe que l'imparfait dans la conception qu'il a faites dans le cadre, et personne n'en parle, donc je suppose que je doit manquer quelque chose.

Le problème, c'est avec AsyncTask. Selon la documentation, il

"permet de faire de l'arrière-plan opérations et publier les résultats sur le Thread de l'INTERFACE utilisateur sans avoir à manipuler les threads et/ou des gestionnaires."

L'exemple continue ensuite à montrer comment certaines exemplaire showDialog() méthode est appelée en onPostExecute(). Ceci, cependant, semble entièrement artificiel pour moi, parce que montrant une boîte de dialogue a toujours besoin d'une référence à un valide Context, et une AsyncTask ne doit jamais tenir une forte référence à un objet de contexte.

La raison en est évidente: si l'activité est détruite, ce qui a déclenché la tâche? Cela peut se produire tout le temps, par exemple parce que vous avez retourné l'écran. Si la tâche de tenir une référence au contexte qui l'a créé, vous n'êtes pas seul tenant à un banal objet de contexte (la fenêtre ont été détruits, et toute interaction de l'INTERFACE utilisateur échoue avec une exception!), vous même, vous risquez de créer une fuite de mémoire.

Sauf si ma logique est erronée, ici, cela se traduit par: onPostExecute() est tout à fait inutile, car à quoi bon, pour cette méthode à exécuter sur le thread de l'INTERFACE utilisateur si vous n'avez pas accès à n'importe quel contexte? Vous ne pouvez pas faire quelque chose de significatif ici.

Une solution de contournement serait de ne pas passer contexte des instances d'une AsyncTask, mais un Handler de l'instance. Qui fonctionne: depuis un Gestionnaire vaguement lie le contexte et la tâche, vous pouvez échanger des messages entre eux sans risquer une fuite (à droite?). Mais cela signifierait que la prémisse de l'AsyncTask, à savoir que vous n'avez pas besoin de s'embêter avec des gestionnaires, est faux. Il semble aussi que d'abuser de Gestionnaire, puisque vous êtes l'envoi et la réception de messages sur le même thread (vous en créer un sur le thread d'INTERFACE utilisateur et de les envoyer à travers elle dans onPostExecute (), qui est également exécuté sur le thread d'INTERFACE utilisateur).

Pour couronner le tout, même avec cette solution de contournement, vous avez encore le problème que lorsque le contexte est détruit, vous avez pas de dossier de toutes les tâches qu'il a tiré. Cela signifie que vous avez à re-démarrer des tâches lors de la re-création du contexte, par exemple, après une orientation de l'écran change. C'est lent et inutile.

Ma solution à ce (tel que mis en œuvre dans le Droid-Fu de la bibliothèque) est de maintenir une cartographie de l' WeakReferences à partir de noms de composants à leurs instances sur l'unique objet de l'application. Chaque fois qu'une AsyncTask est commencé, il enregistre le contexte de l'appel de cette carte, et sur chaque rappel, il va chercher le contexte actuel de l'instance à partir de cette cartographie. Cela garantit que vous ne serez jamais faire référence à un rassis instance de contexte et vous avez toujours accès à un contexte valide dans les rappels de sorte que vous pouvez ne significatif de l'INTERFACE utilisateur d'y travailler. Il n'y a pas de fuite, parce que les références sont faibles et sont effacées lorsque aucune instance d'un composant donné n'existe plus.

Pourtant, c'est une solution plus complexe et nécessite à la sous-classe des de la Droid-Fu de la bibliothèque de classes, ce qui en fait un assez intrusif approche.

Maintenant, je veux simplement savoir: Suis-je tout simplement massivement manque quelque chose ou est AsyncTask vraiment totalement erronée? Comment sont vos expériences de travail avec elle? Comment avez-vous résoudre ces problème?

Merci pour vos commentaires.

86voto

hackbod Points 55292

Que diriez-vous quelque chose comme ce (avertissement non testé) :

20voto

CommonsWare Points 402670
<blockquote> <p>La raison est évidente : que se passe-t-il si l’activité est détruit qui a déclenché la tâche ?</p> <p>Dissocier manuellement l’activité de la <code></code> en <code></code> . Réassocier manuellement la nouvelle activité à le <code></code> en <code></code> . Ceci nécessite soit une classe interne statique ou une classe Java standard, plus peut-être 10 lignes de code.</p></blockquote>

15voto

Il ressemble AsyncTask est un peu plus que juste conceptuellement absurde. Il est également inutilisable par des problèmes de compatibilité. Le Android docs lire:

Lors de sa première apparition, AsyncTasks ont été exécutés en série sur un seul thread d'arrière-plan. En commençant par DONUT, cela a été changé pour un pool de threads qui permet de réaliser plusieurs tâches en parallèle. Départ en NID d'abeille, les tâches sont de retour à être exécuté sur un seul thread pour éviter les erreurs d'application causés par l'exécution en parallèle. Si vous voulez vraiment de l'exécution en parallèle, vous pouvez utiliser l' executeOnExecutor(Executor, Params...) version de cette méthode avec l' THREAD_POOL_EXECUTOR; cependant, voir le commentaire là pour en garde sur son utilisation.

Les deux executeOnExecutor() et THREAD_POOL_EXECUTOR sont Ajoutés dans l'API de niveau 11 (Android 3.0.x, en NID d'abeille).

Cela signifie que si vous créez deux AsyncTasks pour télécharger les deux fichiers, le 2ème de téléchargement ne démarre pas jusqu'à ce que le premier se termine. Si vous chat via deux serveurs, et le premier serveur est en panne, vous ne vous connectez pas au second, avant la connexion pour la première fois. (Sauf si vous utilisez la nouvelle API11 caractéristiques, bien sûr, mais cela permet de rendre votre code incompatible avec les 2.x).

Et si vous souhaitez cibler à la fois les 2.x et 3.0+, la substance devient vraiment difficile.

En outre, les docs disent:

Attention: un Autre problème que vous pouvez rencontrer lors de l'utilisation d'un thread de travail est inattendu redémarrage de l'activité en raison d'une configuration à l'exécution du changement (par exemple lorsque l'utilisateur modifie l'orientation de l'écran), ce qui peut détruire votre thread de travail. Pour voir comment vous pouvez conserver votre tâche lors de l'une de ces redémarre et comment annuler la tâche lorsque l'activité est détruite, voir le code source pour les Étagères exemple d'application.

12voto

Probablement, nous tous, y compris Google, utilisent frauduleusement AsyncTask de la MVC point de vue.

Une Activité est un Contrôleuret le contrôleur ne doit pas commencer les opérations qui peuvent survivre à la Vue. C'est, AsyncTasks doit être utilisé de Modèle, à partir d'une classe qui n'est pas lié à l'Activité du cycle de vie -- rappelez-vous que les Activités sont détruits lors de la rotation. (Comme pour la Vue, vous n'avez pas l'habitude de programme des classes dérivées, par exemple, le android.widget de.Bouton, mais vous pouvez. Habituellement, la seule chose que vous faites au sujet de la Vue est le xml.)

En d'autres termes, il est faux de place AsyncTask dérivés dans les méthodes d'Activités. Otoh, que, si nous ne devons pas utiliser AsyncTasks dans les Activités, les AsyncTask perd de son attractivité: il sert à être annoncé comme un moyen rapide et facile de la corriger.

5voto

oli Points 2335

Je ne sais pas qu'il est vrai que vous risquez une fuite de mémoire avec une référence à un contexte d’une AsyncTask.

La manière habituelle de leur mise en œuvre est de créer une nouvelle instance de AsyncTask dans le cadre de l’une des méthodes de l’activité. Donc si l’activité est détruite, puis une fois terminée la AsyncTask ne serait-il inaccessible et puis éligibles pour le garbage collection ? Si la référence à l’activité ne sera pas question parce que le AsyncTask lui-même ne sera pas traîner.

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