108 votes

Moyen idéal d'annuler une AsyncTask en cours d'exécution

Je lance des opérations de récupération de fichiers audio à distance et de lecture de fichiers audio dans un thread arrière-plan en utilisant AsyncTask. Une barre de progression Cancellable est affichée pendant le temps que dure l'opération de récupération.

Je veux annuler/interrompre l'exécution de AsyncTask lorsque l'utilisateur annule (décide contre) l'opération. Quelle est la meilleure façon de gérer un tel cas?

77voto

yanchenko Points 24142

Vient de découvrir que AlertDialogs boolean cancel(...); que j'ai utilisé partout en fait ne fait rien. Génial.
Donc...

public class MyTask extends AsyncTask {

    private volatile boolean running = true;
    private final ProgressDialog progressDialog;

    public MyTask(Context ctx) {
        progressDialog = gimmeOne(ctx);

        progressDialog.setCancelable(true);
        progressDialog.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                // pourrait réellement définir running = false; juste ici, mais je vais
                // respecter le contrat.
                cancel(true);
            }
        });

    }

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }

    @Override
    protected void onCancelled() {
        running = false;
    }

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

        while (running) {
            // fait le travail difficile
        }
        return null;
    }

    // ...

}

0 votes

De plus, j'ai appelé la méthode cancel sur l'AsyncTask

55 votes

Au lieu de créer un indicateur booléen pour "running", ne pourriez-vous pas le supprimer et faire cela ?

36 votes

À partir de la documentation sur onCancelled(): "S'exécute sur le thread UI après l'annulation(boolean) est invoquée et doInBackground(Objet[]) est terminé.". Ce 'après' signifie que définir un drapeau dans onCancelled et vérifier dans doInBackground n'a aucun sens.

76voto

wrygiel Points 1857

Si vous effectuez des calculs:

  • Vous devez vérifier périodiquement isCancelled().

Si vous effectuez une requête HTTP:

  • Sauvegardez l'instance de votre HttpGet ou HttpPost quelque part (par exemple, dans un champ public).
  • Après avoir appelé cancel, appelez request.abort(). Cela provoquera une exception IOException lancée à l'intérieur de votre méthode doInBackground.

Dans mon cas, j'avais une classe connecteur que j'utilisais dans diverses AsyncTasks. Pour simplifier les choses, j'ai ajouté une nouvelle méthode abortAllRequests à cette classe et j'ai appelé cette méthode directement après avoir appelé cancel.

0 votes

Merci, ça marche, mais comment éviter l'exception dans ce cas?

0 votes

Vous devez appeler HttpGet.abort() à partir d'un thread arrière-plan ou vous obtiendrez une android.os.NetworkOnMainThreadException.

0 votes

@wrygiel Si vous effectuez une requête HTTP, ne devrait pas cancel(true) interrompre la requête? D'après la documentation: Si la tâche a déjà commencé, alors le paramètre mayInterruptIfRunning détermine si le thread exécutant cette tâche doit être interrompu dans une tentative d'arrêter la tâche.

20voto

DonCroco Points 111

La chose est que l'appel AsyncTask.cancel() n'appelle que la fonction onCancel dans votre tâche. C'est là que vous voulez gérer la demande d'annulation.

Voici une petite tâche que j'utilise pour déclencher une méthode de mise à jour

private class UpdateTask extends AsyncTask {

        private boolean running = true;

        @Override
        protected void onCancelled() {
            running = false;
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
            onUpdate();
        }

        @Override
        protected Void doInBackground(Void... params) {
             while(running) {
                 publishProgress();
             }
             return null;
        }
     }

2 votes

Cela fonctionnera, mais logiquement, lorsque vous attendez la réponse du serveur et que vous venez de réaliser une opération de base de données, alors cela devrait se refléter correctement dans votre activité. J'ai écrit un blog à ce sujet, voir ma réponse.

4 votes

Comme mentionné dans les commentaires de la réponse acceptée, il n'est pas nécessaire de créer votre propre drapeau running. AsyncTask possède un drapeau interne qui est défini lorsque la tâche a été annulée. Remplacez while (running) par while (!isCancelled()). [developer.android.com/reference/android/os/…](http://developer.android.com/reference/android/os/AsyncTask.html#isCancelled()) Ainsi, dans ce cas simple, vous n'avez pas besoin de remplacer onCancelled().

11voto

CommonsWare Points 402670

Simple : n'utilisez pas un AsyncTask. AsyncTask est conçu pour des opérations courtes qui se terminent rapidement (des dizaines de secondes) et qui n'ont donc pas besoin d'être annulées. La "lecture de fichiers audio" ne rentre pas dans ce cas. Vous n'avez même pas besoin d'un thread en arrière-plan pour une lecture ordinaire de fichiers audio.

0 votes

Suggérez-vous que nous utilisions un thread Java régulier et que nous "interrompions" l'exécution du thread en utilisant une variable booléenne volatile - la manière Java conventionnelle ?

0 votes

Je ne sais pas ce que signifie volatile - j'ai tendance à utiliser des objets java.util.concurrent pour ce genre de choses.

34 votes

Pas d'offense Mike, mais ce n'est pas une réponse acceptable. AsyncTask possède une méthode d'annulation, et cela devrait fonctionner. Autant que je sache, ça ne fonctionne pas - mais même si je le fais mal, alors il devrait y avoir une bonne façon d'annuler une tâche. La méthode n'existerait pas sinon. Et même les tâches courtes peuvent avoir besoin d'être annulées - j'ai une Activity où elle commence immédiatement une AsyncTask lors du chargement, et si l'utilisateur appuie sur retour immédiatement après l'ouverture de la tâche, il verra une fermeture forcée une seconde plus tard lorsque la tâche se termine mais aucun contexte n'existe pour l'utiliser dans son onPostExecute.

4voto

dbyrne Points 18604

La seule façon de le faire est de vérifier la valeur de la méthode isCancelled() et d'arrêter la lecture lorsque celle-ci retourne true.

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