web-dev-qa-db-fra.com

Android AsyncTask pour les opérations de longue durée

Citant la documentation pour AsyncTask trouvé ici , il dit:

AsyncTasks devrait idéalement être utilisé pour des opérations courtes (quelques secondes tout au plus.) Si vous devez garder les threads en cours d'exécution pendant de longues périodes, il est fortement recommandé d'utiliser les différentes API fournies par le pacakge Java.util.concurrent, telles que Executor, ThreadPoolExecutor et FutureTask.

Maintenant ma question se pose: pourquoi? La fonction doInBackground() s'exécute sur le thread d'interface utilisateur, alors quel mal y a-t-il à avoir une opération de longue durée ici?

88
user1730789

C'est une très bonne question, cela prend du temps en tant que programmeur Android pour bien comprendre le problème. En effet, AsyncTask a deux problèmes principaux qui sont liés:

  • Ils sont mal liés au cycle de vie de l'activité
  • Ils créent très facilement des fuites de mémoire.

Dans l'application RoboSpice Motivations ( disponible sur Google Play ) nous répondons à cette question en détail. Il vous donnera une vue approfondie des AsyncTasks, des chargeurs, de leurs fonctionnalités et de leurs inconvénients et vous présentera également une solution alternative pour les demandes réseau: RoboSpice. Les demandes de réseau sont une exigence courante dans Android et sont par nature des opérations de longue durée. Voici un extrait de l'application:

Le cycle de vie AsyncTask et Activity

Les AsyncTasks ne suivent pas le cycle de vie des instances d'activité. Si vous démarrez une AsyncTask dans une activité et que vous faites pivoter le périphérique, l'activité sera détruite et une nouvelle instance sera créée. Mais l'AsyncTask ne mourra pas. Il continuera à vivre jusqu'à ce qu'il se termine.

Et une fois terminé, AsyncTask ne mettra pas à jour l'interface utilisateur de la nouvelle activité. En effet il met à jour l'ancienne instance de l'activité qui n'est plus affichée. Cela peut entraîner une exception de type Java.lang.IllegalArgumentException: vue non attachée au gestionnaire de fenêtres si vous utilisez, par exemple, findViewById pour récupérer une vue à l'intérieur de l'activité.

Problème de fuite de mémoire

Il est très pratique de créer des AsyncTasks en tant que classes internes de vos activités. Comme la tâche AsyncTask devra manipuler les vues de l'activité lorsque la tâche est terminée ou en cours, l'utilisation d'une classe interne de l'activité semble pratique: les classes internes peuvent accéder directement à n'importe quel champ de la classe externe.

Néanmoins, cela signifie que la classe interne contiendra une référence invisible sur son instance de classe externe: l'activité.

À long terme, cela produit une fuite de mémoire: si la tâche AsyncTask dure longtemps, elle maintient l'activité "vivante" alors que Android voudrait s'en débarrasser car elle ne peut plus être affichée L'activité ne peut pas être récupérée et c'est un mécanisme central pour Android pour préserver les ressources sur l'appareil.


C'est vraiment une très très mauvaise idée d'utiliser AsyncTasks pour des opérations de longue durée. Néanmoins, ils sont parfaits pour ceux de courte durée comme la mise à jour d'une vue après 1 ou 2 secondes.

Je vous encourage à télécharger RoboSpice Motivations app , il explique vraiment cela en profondeur et fournit des exemples et des démonstrations des différentes façons d'effectuer certaines opérations d'arrière-plan.

118
Snicolas

pourquoi ?

Parce que AsyncTask, par défaut, utilise un pool de threads que vous n'avez pas créé. N'attachez jamais de ressources d'un pool que vous n'avez pas créé, car vous ne savez pas quelles sont les exigences de ce pool. Et ne bloquez jamais les ressources d'un pool que vous n'avez pas créé si la documentation de ce pool vous dit de ne pas le faire, comme c'est le cas ici.

En particulier, en commençant par Android 3.2, le pool de threads utilisé par AsyncTask par défaut (pour les applications avec Android:targetSdkVersion réglé sur 13 ou plus) ne contient que n thread - si vous bloquez ce thread indéfiniment, aucune de vos autres tâches ne s'exécutera.

38
CommonsWare

Les tâches Aysnc sont des threads spécialisés qui sont toujours destinés à être utilisés avec l'interface graphique de vos applications, tout en conservant les tâches gourmandes en ressources du thread d'interface utilisateur. Donc, lorsque des choses comme la mise à jour des listes, la modification de vos vues, etc. nécessitent que vous effectuiez des opérations de récupération ou de mise à jour, vous devez utiliser des tâches asynchrones afin de pouvoir garder ces opérations hors du thread d'interface utilisateur, mais notez que ces opérations sont toujours connectées à l'interface utilisateur d'une manière ou d'une autre .

Pour les tâches plus longues, qui ne nécessitent pas de mise à jour de l'interface utilisateur, vous pouvez utiliser des services à la place, car ils peuvent vivre même sans interface utilisateur.

Donc, pour les tâches courtes, utilisez des tâches asynchrones car elles peuvent être tuées par le système d'exploitation après la fin de votre activité de frai (généralement ne mourra pas en cours d'opération mais terminera sa tâche). Et pour les tâches longues et répétitives, utilisez plutôt les services.

pour plus d'informations, voir les discussions:

AsyncTask pendant plus de quelques secondes?

et

AsyncTask ne s'arrêtera pas même lorsque l'activité a été détruite

4
Anup Cowkur

Le problème avec AsyncTask est que s'il est défini comme classe interne non statique de l'activité, il aura une référence à l'activité. Dans le scénario où l'activité du conteneur de la tâche asynchrone se termine, mais le travail en arrière-plan dans AsyncTask continue, l'objet d'activité ne sera pas récupéré car il y a une référence, cela provoque la fuite de mémoire.

La solution pour résoudre ce problème est de définir tâche asynchrone en tant que classe interne statique d'activité et d'utiliser une référence faible au contexte.

Mais quand même, c'est une bonne idée de l'utiliser pour des tâches de fond simples et rapides. Pour développer une application avec du code propre, il est préférable d'utiliser RxJava pour exécuter des tâches d'arrière-plan complexes et mettre à jour l'interface utilisateur avec les résultats.

1
Arnav Rao