J'ai étudié ce problème depuis des mois maintenant, j'ai trouvé différentes solutions, ce dont je ne suis pas satisfait car ce sont tous des hacks massifs. Je ne peux toujours pas croire qu'une classe qui a des défauts de conception soit entrée dans le cadre et que personne n'en parle, alors je suppose que je dois manquer quelque chose.
Le problème est avec AsyncTask
. Selon la documentation, il
"permet d'effectuer des opérations en arrière-plan et de publier les résultats sur le thread d'interface utilisateur sans avoir à manipuler les threads et / ou les gestionnaires."
L'exemple continue ensuite de montrer comment une showDialog()
méthode exemplaire est appelée onPostExecute()
. Cela, cependant, me semble entièrement artificiel , car afficher une boîte de dialogue a toujours besoin d'une référence à un valide Context
, et une AsyncTask ne doit jamais contenir une référence forte à un objet de contexte .
La raison est évidente: que se passe-t-il si l'activité est détruite, ce qui a déclenché la tâche? Cela peut arriver tout le temps, par exemple parce que vous avez renversé l'écran. Si la tâche contient une référence au contexte qui l'a créée, vous ne vous accrochez pas seulement à un objet de contexte inutile (la fenêtre aura été détruite et toute interaction d'interface utilisateur échouera à une exception!), Vous risquez même de créer un fuite de mémoire.
À moins que ma logique ne soit défectueuse ici, cela se traduit par: onPostExecute()
est entièrement inutile, car à quoi sert cette méthode de s'exécuter sur le thread d'interface utilisateur si vous n'avez accès à aucun contexte? Vous ne pouvez rien faire de significatif ici.
Une solution de contournement consisterait à ne pas passer d'instances de contexte à une AsyncTask, mais à une Handler
instance. Cela fonctionne: puisqu'un gestionnaire lie vaguement le contexte et la tâche, vous pouvez échanger des messages entre eux sans risquer une fuite (non?). Mais cela signifierait que la prémisse d'AsyncTask, à savoir que vous n'avez pas besoin de vous soucier des gestionnaires, est erronée. Cela semble également abuser de Handler, car vous envoyez et recevez des messages sur le même thread (vous le créez sur le thread d'interface utilisateur et l'envoyez via 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 toujours le problème que lorsque le contexte est détruit, vous n'avez aucun enregistrement des tâches qu'il a déclenchées. Cela signifie que vous devez redémarrer toutes les tâches lors de la recréation du contexte, par exemple après un changement d'orientation de l'écran. C'est lent et inutile.
Ma solution à cela (telle qu'implémentée dans la bibliothèque Droid-Fu ) est de maintenir un mappage des WeakReference
s des noms de composants à leurs instances actuelles sur l'objet d'application unique. Chaque fois qu'un AsyncTask est démarré, il enregistre le contexte appelant dans cette carte, et à chaque rappel, il récupère l'instance de contexte actuelle à partir de cette correspondance. Cela garantit que vous ne référencerez jamais une instance de contexte périmé et que vous aurez toujours accès à un contexte valide dans les rappels afin que vous puissiez y effectuer un travail d'interface utilisateur significatif. Il ne fuit pas non plus, car les références sont faibles et sont effacées lorsqu'aucune instance d'un composant donné n'existe plus.
Pourtant, c'est une solution de contournement complexe et nécessite de sous-classer certaines des classes de la bibliothèque Droid-Fu, ce qui en fait une approche assez intrusive.
Maintenant, je veux simplement savoir: suis-je simplement en train de manquer quelque chose ou AsyncTask est-il vraiment entièrement défectueux? Comment vos expériences fonctionnent-elles avec lui? Comment avez-vous résolu ce problème?
Merci pour votre contribution.
la source
Réponses:
Que diriez-vous quelque chose comme ça:
la source
Dissociez manuellement l'activité de l'
AsyncTask
intérieuronDestroy()
. Réassociez manuellement la nouvelle activité à l'AsyncTask
entréeonCreate()
. Cela nécessite une classe interne statique ou une classe Java standard, plus peut-être 10 lignes de code.la source
Il semble que ce
AsyncTask
soit un peu plus que des défauts conceptuels . Il est également inutilisable en raison de problèmes de compatibilité. Les documents Android lisent:Lors de leur introduction, les AsyncTasks ont été exécutés en série sur un seul thread d'arrière-plan. À partir de DONUT, cela a été changé en un pool de threads permettant à plusieurs tâches de fonctionner en parallèle. À partir de HONEYCOMB, les tâches sont de nouveau exécutées sur un seul thread pour éviter les erreurs d'application courantes causées par l'exécution parallèle. Si vous voulez vraiment une exécution parallèle, vous pouvez utiliser la
executeOnExecutor(Executor, Params...)
version de cette méthode avecTHREAD_POOL_EXECUTOR
; cependant, voir les commentaires pour les avertissements sur son utilisation.Les deux
executeOnExecutor()
etTHREAD_POOL_EXECUTOR
sont ajoutés dans l'API niveau 11 (Android 3.0.x, HONEYCOMB).Cela signifie que si vous créez deux
AsyncTask
s pour télécharger deux fichiers, le 2e téléchargement ne démarrera pas avant la fin du premier. Si vous discutez via deux serveurs et que le premier serveur est en panne, vous ne vous connecterez pas au second avant que la connexion au premier n'expire. (À moins que vous n'utilisiez les nouvelles fonctionnalités d'API11, bien sûr, mais cela rendra votre code incompatible avec 2.x).Et si vous souhaitez cibler à la fois 2.x et 3.0+, les choses deviennent vraiment délicates.
De plus, les documents disent:
Attention: Un autre problème que vous pourriez rencontrer lors de l'utilisation d'un thread de travail est le redémarrage inattendu de votre activité en raison d'un changement de configuration d'exécution (tel que lorsque l'utilisateur change l'orientation de l'écran), ce qui peut détruire votre thread de travail . Pour voir comment vous pouvez conserver votre tâche pendant l'un de ces redémarrages et comment annuler correctement la tâche lorsque l'activité est détruite, consultez le code source de l'exemple d'application Shelves.
la source
Probablement nous tous, y compris Google, abusons
AsyncTask
du point de vue MVC .Une activité est un contrôleur , et le contrôleur ne doit pas démarrer d'opérations susceptibles de survivre à la vue . Autrement dit, les AsyncTasks doivent être utilisés à partir de Model , à partir d'une classe qui n'est pas liée au cycle de vie de l'activité - rappelez-vous que les activités sont détruites lors de la rotation. (En ce qui concerne la vue , vous ne programmez généralement pas de classes dérivées, par exemple, de android.widget.Button, mais vous le pouvez. Habituellement, la seule chose que vous faites à propos de la vue est le xml.)
En d'autres termes, il est faux de placer des dérivés AsyncTask dans les méthodes d'Activités. OTOH, si nous ne devons pas utiliser AsyncTasks dans les activités, AsyncTask perd son attrait: il était auparavant annoncé comme une solution rapide et facile.
la source
Je ne suis pas sûr qu'il soit vrai que vous risquez une fuite de mémoire avec une référence à un contexte à partir d'une AsyncTask.
La manière habituelle de les implémenter est de créer une nouvelle instance AsyncTask dans le cadre de l'une des méthodes de l'activité. Donc, si l'activité est détruite, une fois la tâche AsyncTask terminée, ne sera-t-elle pas accessible et pourra-t-elle être récupérée? La référence à l'activité n'aura donc aucune importance car la tâche AsyncTask elle-même ne restera pas en place.
la source
Il serait plus robuste de conserver une semaine de référence sur votre activité:
la source
Pourquoi ne pas simplement remplacer la
onPause()
méthode dans l'activité propriétaire et annuler la àAsyncTask
partir de là?la source
onPause
ce qu'il est tout aussi mauvais que de le maintenir ailleurs? C'est à dire, vous pourriez obtenir un ANR?onPause
ou autre) car nous risquons d'obtenir un ANR.Vous avez tout à fait raison - c'est pourquoi l'abandon de l'utilisation des tâches / chargeurs asynchrones dans les activités pour récupérer des données prend de l'ampleur. L'une des nouvelles façons consiste à utiliser un framework Volley qui fournit essentiellement un rappel une fois que les données sont prêtes - beaucoup plus cohérent avec le modèle MVC. Volley a été peuplé dans Google I / O 2013. Je ne sais pas pourquoi plus de gens ne le savent pas.
la source
Personnellement, je viens d'étendre Thread et d'utiliser une interface de rappel pour mettre à jour l'interface utilisateur. Je n'ai jamais pu faire fonctionner AsyncTask sans problèmes FC. J'utilise également une file d'attente non bloquante pour gérer le pool d'exécution.
la source
Je pensais que l'annulation fonctionne, mais ce n'est pas le cas.
ici, ils RTFMing à ce sujet:
"" Si la tâche a déjà démarré, le paramètre mayInterruptIfRunning détermine si le thread exécutant cette tâche doit être interrompu pour tenter d'arrêter la tâche. "
Cela n'implique cependant pas que le thread soit interruptible. C'est une chose Java, pas une chose AsyncTask. "
http://groups.google.com/group/android-developers/browse_thread/thread/dcadb1bc7705f1bb/add136eb4949359d?show_docid=add136eb4949359d
la source
Vous feriez mieux de penser à AsyncTask comme quelque chose qui est plus étroitement associé à une activité, un contexte, un ContextWrapper, etc. C'est plus pratique lorsque sa portée est pleinement comprise.
Assurez-vous que vous avez une politique d'annulation dans votre cycle de vie afin qu'elle finisse par être récupérée et ne conserve plus de référence à votre activité et qu'elle puisse également être récupérée.
Sans annuler votre AsyncTask pendant que vous vous éloignez de votre contexte, vous rencontrerez des fuites de mémoire et des NullPointerExceptions, si vous avez simplement besoin de fournir des commentaires comme un Toast une simple boîte de dialogue, puis un singleton de votre contexte d'application aiderait à éviter le problème NPE.
AsyncTask n'est pas si mal, mais il y a certainement beaucoup de magie qui peut conduire à des pièges imprévus.
la source
Quant aux «expériences de travail avec lui»: il est possible de tuer le processus avec tous les AsyncTasks, Android va recréer la pile d'activités afin que l'utilisateur ne mentionne rien.
la source