web-dev-qa-db-fra.com

Exécuter AsyncTask plusieurs fois

Dans mon activité, j'utilise une classe qui s'étend de AsyncTask et un paramètre qui est une instance de cet AsyncTask. Quand j'appelle mInstanceOfAT.execute("") tout va bien. Mais l'application se bloque lorsque j'appuie sur un bouton de mise à jour qui appelle à nouveau l'AsyncTask (au cas où le travail réseau ne fonctionnerait pas). La cause apparaît alors une exception qui dit

Impossible d'exécuter la tâche: la tâche a déjà été exécutée (une tâche ne peut être exécutée qu'une seule fois)

J'ai essayé d'appeler cancel (true) pour l'instance de Asyctask, mais cela ne fonctionne pas non plus. Jusqu'à présent, la seule solution consiste à créer de nouvelles instances d'Asyntask. Est-ce la bonne façon?

Merci.

127
Dayerman

AsyncTask les instances ne peuvent être utilisées qu'une seule fois.

Au lieu de cela, appelez simplement votre tâche comme new MyAsyncTask().execute("");

À partir de la documentation de l'API AsyncTask:

Règles de filetage

Quelques règles de threading doivent être suivies pour que cette classe fonctionne correctement:

  • L'instance de tâche doit être créée sur le thread d'interface utilisateur.
  • execute (Params ...) doit être appelé sur le thread d'interface utilisateur.
  • N'appelez pas onPreExecute (), onPostExecute (Result), doInBackground (Params ...), onProgressUpdate (Progress ...) manuellement.
  • La tâche ne peut être exécutée qu'une seule fois (une exception sera levée si une deuxième exécution est tentée.)
217
Steve Prentice

Les raisons pour lesquelles les instances ASyncTask sont déclenchées et reprises sont détaillées dans la réponse de Steve Prentice - Cependant, même si vous êtes limité sur combien de fois vous exécutez une ASyncTask, vous êtes libre de faire ce que vous aimez pendant que le fil coule ...

Placez votre code exécutable dans une boucle entre doInBackground () et utilisez un verrou simultané pour déclencher chaque exécution. Vous pouvez récupérer les résultats en utilisant publishProgress ()/onProgressUpdate ().

Exemple:

class GetDataFromServerTask extends AsyncTask<Input, Result, Void> {

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition tryAgain = lock.newCondition();
    private volatile boolean finished = false;

    @Override
    protected Void doInBackground(Input... params) {

        lock.lockInterruptibly();

        do { 
            // This is the bulk of our task, request the data, and put in "result"
            Result result = ....

            // Return it to the activity thread using publishProgress()
            publishProgress(result);

            // At the end, we acquire a lock that will delay
            // the next execution until runAgain() is called..
            tryAgain.await();

        } while(!finished);

        lock.unlock();
    }

    @Override
    protected void onProgressUpdate(Result... result) 
    {
        // Treat this like onPostExecute(), do something with result

        // This is an example...
        if (result != whatWeWant && userWantsToTryAgain()) {
            runAgain();
        }
    }

    public void runAgain() {
        // Call this to request data from the server again
        tryAgain.signal();
    }

    public void terminateTask() {
        // The task will only finish when we call this method
        finished = true;
        lock.unlock();
    }

    @Override
    protected void onCancelled() {
        // Make sure we clean up if the task is killed
        terminateTask();
    }
}

Bien sûr, ceci est légèrement plus compliqué que l'utilisation traditionnelle de ASyncTask, et vous abandonnez l'utilisation de publishProgress () pour les rapports d'avancement réels. Mais si la mémoire vous préoccupe, cette approche garantira qu’un seul ASyncTask reste dans le tas au moment de l’exécution.

28
seanhodges

J'ai eu le même problème. Dans mon cas, j'ai une tâche que je veux faire dans onCreate() et dans onResume(). J'ai donc créé mon Asynctask statique et en obtenir l'instance. Maintenant, nous avons toujours le même problème.

Donc, ce que j'ai fait dans onPostExecute () est la suivante:

instance = null;

Gardant à l'esprit que je vérifie dans la méthode statique getInstance que mon instance n'est pas nulle, sinon je la crée:

if (instance == null){
    instance = new Task();
}
return instance;

La méthode dans postExecute videra l'instance et la recréera. Bien sûr, cela peut être fait en dehors de la classe.

2
SamuelD

J'ai rendu mes tâches de rotation statiques, ce qui m'a ensuite aidé à les attacher, à les détacher et à les rattacher aux threads d'interface utilisateur lors des changements de rotation. Mais pour revenir à votre question, ce que je fais est de créer un drapeau pour voir si le fil est en cours d'exécution. Lorsque vous voulez redémarrer le thread, je vérifie si la tâche de rotation est en cours d'exécution, si c'est moi qui porte un avertissement. Si ce n'est pas le cas, je le supprime et puis j'en crée un nouveau qui contournera l'erreur que vous constatez. De plus, si l'opération est réussie, j'annule la tâche consciente de la rotation terminée afin qu'elle soit prête à reprendre.

1
Arnab C.

Oui c'est vrai, le doc dit que seulement peut être exécuté un Asyntask.

Chaque fois que vous avez besoin de l'utiliser, vous devez par exemple:

// Any time if you need to call her
final FirmwareDownload fDownload = new FirmwareDownload();
fDownload.execute("your parameter");

static class FirmwareDownload extends AsyncTask<String, String, String> {
}
0
Victor Ruiz C.