92 votes

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

En citant la documentation pour AsyncTask, on trouve ici il est dit :

Les AsyncTasks doivent idéalement être utilisées pour des opérations de courte durée (quelques secondes tout au plus). Si vous devez maintenir des threads en cours d'exécution pendant de longues périodes, il est fortement recommandé d'utiliser les diverses API fournies par le paquet java.util.concurrent telles que Executor, ThreadPoolExecutor et FutureTask.

Maintenant, ma question se pose : pourquoi ? Le site doInBackground() s'exécute hors du thread de l'interface utilisateur, alors quel mal y a-t-il à avoir une opération longuement exécutée ici ?

2 votes

Le problème auquel j'ai été confronté lors du déploiement d'une application (qui utilise asyncTask) sur un appareil réel était que l'application longue durée doInBackground fige l'écran si une barre de progression n'est pas utilisée.

2 votes

Parce qu'une AsyncTask est liée à l'activité dans laquelle elle est lancée. Donc si l'activité est tuée, votre instance AsyncTask peut être tuée aussi.

0 votes

Et si je créais l'AsyncTask dans un Service ? Cela ne résoudrait-il pas le problème ?

121voto

Snicolas Points 19644

C'est une très bonne question, il faut 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 peu liés au cycle de vie de l'activité
  • Ils créent très facilement des fuites de mémoire.

A l'intérieur de la RoboSpice Application Motivations ( disponible sur Google Play ) nous répondons à cette question en détail. Il vous donnera une vue approfondie des AsyncTasks, des Loaders, de leurs caractéristiques et de leurs inconvénients et vous présentera également une solution alternative pour les requêtes réseau : RoboSpice. Les requêtes réseau sont une exigence commune dans Android et sont par nature des opérations de longue durée. . Voici un extrait de l'application :

Le cycle de vie des AsyncTask et des Activity

Les AsyncTasks ne suivent pas le cycle de vie des Activity instances. Si vous lancez une AsyncTask à l'intérieur d'une Activity et que vous faites pivoter le dispositif, l'Activity sera détruite et une nouvelle instance sera créée. Mais l'AsyncTask ne mourra pas. Elle continuera à vivre jusqu'à ce qu'elle soit terminée.

Et lorsqu'elle se termine, l'AsyncTask ne met pas à jour l'interface utilisateur de la nouvelle activité. En effet, elle met à jour l'ancienne instance de l'activité qui n'est plus affichée. Cela peut conduire à une Exception du type java.lang.IllegalArgumentException : View not attached to window manager si vous utilisez, par exemple, findViewById pour récupérer une vue dans l'activité.

Problème de fuite de mémoire

Il est très pratique de créer des AsyncTasks comme classes internes de vos activités. Comme l'AsyncTask devra manipuler les vues de l'Activity lorsque la tâche sera terminée ou en cours, l'utilisation d'une classe interne de l'Activity semble pratique. 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 à tous les champs de la classe externe. accéder directement à n'importe quel champ de la classe externe.

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

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


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

Je vous encourage à télécharger le Application RoboSpice Motivations Il explique vraiment tout cela en profondeur et fournit des exemples et des démonstrations des différentes manières d'effectuer certaines opérations en arrière-plan.

0 votes

@Snicolas Bonjour, j'ai une application qui scanne les données d'un tag NFC et les envoie au serveur. Il fonctionne bien dans les zones de bon signal, mais là où il n'y a pas de signal, l'AsyncTask qui fait l'appel web continue de fonctionner. Par exemple, la boîte de dialogue de progression fonctionne pendant des minutes et quand elle disparaît, l'écran devient noir et ne répond plus. Mon AsyncTask est une classe interne. J'écris un Handler pour annuler la tâche après X secondes. L'application semble envoyer des données anciennes au serveur plusieurs heures après le scan. Cela pourrait-il être dû au fait que l'AsyncTask ne se termine pas, puis se termine des heures plus tard ? J'apprécierais tout commentaire.

0 votes

Tracez pour voir ce qui se passe. Mais oui, c'est tout à fait possible ! Si vous concevez bien votre asynchronisme, vous pouvez l'annuler assez correctement, ce serait un bon point de départ si vous ne voulez pas migrer vers RS ou un service...

0 votes

@Snicolas Merci pour la réponse. J'ai posté un message sur SO hier pour exposer mon problème et montrer le code du gestionnaire que j'ai écrit pour essayer d'arrêter l'AsyncTask après 8 secondes. Pourriez-vous y jeter un coup d'œil, si vous avez le temps ? Le fait d'appeler AsyncTask.cancel(true) à partir d'un Handler va-t-il annuler la tâche correctement ? Je sais que je devrais vérifier périodiquement la valeur de iscancelled() dans mon doInBackgroud, mais je ne pense pas que cela s'applique à mes circonstances car je fais juste un appel web HttpPost d'une ligne et je ne publie pas les mises à jour sur l'interface utilisateur.

38voto

CommonsWare Points 402670

Pourquoi ?

Parce que AsyncTask par défaut, utilise un pool de threads que vous n'avez pas créé . N'immobilisez jamais les ressources d'un pool que vous n'avez pas créé, car vous ne savez pas quelles sont les exigences de ce pool. Et n'affectez 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, à partir d'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) n'a que un Si vous bloquez ce fil indéfiniment, aucune de vos autres tâches ne s'exécutera.

0 votes

Merci pour cette explication.. Je n'ai rien trouvé de vraiment défectueux dans l'utilisation d'AsyncTasks pour les opérations de longue durée (bien que je délègue généralement ces opérations à des services moi-même), mais votre argument sur l'immobilisation de ce ThreadPool (pour lequel vous ne pouvez en effet faire aucune hypothèse sur sa taille) semble parfait. En complément du post d'Anup sur l'utilisation d'un service : Ce service devrait lui-même aussi exécuter la tâche sur un thread d'arrière-plan, et ne pas bloquer son propre thread principal. Une option pourrait être un IntentService, ou, pour des exigences de concurrence plus complexes, appliquer votre propre stratégie de multithreading.

0 votes

Est-ce que c'est la même chose si je lance l'AsyncTask dans un Service ? Je veux dire, l'opération de longue durée serait-elle toujours un problème ?

1 votes

@eddy : Oui, car cela ne change pas la nature du pool de fils. Pour un Service il suffit d'utiliser un Thread ou un ThreadPoolExecutor .

4voto

Anup Cowkur Points 11763

Les tâches Aysnc sont des threads spécialisés qui sont toujours destinés à être utilisés avec l'interface graphique de votre application, mais tout en conservant les tâches lourdes en ressources du thread de l'interface graphique. Ainsi, lorsque des choses comme la mise à jour de listes, la modification de vos vues, etc., nécessitent des opérations de récupération ou de mise à jour, vous devez utiliser des tâches asynchrones pour que ces opérations ne soient pas effectuées par le thread de l'interface utilisateur, mais notez que ces opérations sont toujours connectées à l'interface utilisateur.

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

Ainsi, 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 mort de l'activité qui les génère (en général, elles ne meurent pas au milieu de l'opération mais terminent leur tâche). Et pour les tâches longues et répétitives, utilisez plutôt des services.

Pour plus d'informations, voir les fils :

AsyncTask pendant plus de quelques secondes ?

et

AsyncTask ne s'arrête pas même lorsque l'activité est détruite

1voto

Arnav Rao Points 2156

Le problème avec AsyncTask est que si elle est définie comme une classe interne non statique de l'activité, elle aura une référence à l'activité. Dans le scénario où l'activité, le conteneur de la tâche asynchrone, se termine, mais que le travail en arrière-plan dans AsyncTask continue, l'objet activité ne sera pas ramassé car il y a une référence à lui, ce qui cause une 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'utilisation faible référence au contexte.

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

-1voto

silverback Points 3108

Il existe plusieurs façons d'effectuer une opération intensive dans un thread séparé. Selon les scénarios, cela diffère. J'ai utilisé Handler

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