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.