web-dev-qa-db-fra.com

Android: Annuler la tâche asynchrone

J'utilise une tâche asynchrone pour télécharger une image et obtenir des résultats.

Pendant le téléchargement de l'image, je vois une boîte de dialogue de progression, écrite dans la méthode onPreExecute () comme ceci:

    protected void onPreExecute() { 
         uploadingDialog = new ProgressDialog(MyActivity.this); 
         uploadingDialog.setMessage("uploading"); 
         uploadingDialog.setCancelable(true);
         uploadingDialog.show();
    }

Ok quand j'appuie sur le bouton retour, le dialogue disparaît évidemment à cause de setCancelable (true).

Mais (évidemment), la tâche asynchrone ne s’arrête pas.

Alors, comment puis-je résoudre ce problème? Je souhaite annuler les tâches de dialogue et asynchrones lorsque j'appuie sur le bouton Précédent. Des idées?

EDIT: TROUVE LA SOLUTION. VOIR MA REPONSE CI-DESSOUS.

56
MScott

TROUVE LA SOLUTION: J'ai ajouté un écouteur d'action avant de téléverserDialog.show () comme ceci:

    uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
          public void onCancel(DialogInterface dialog) {
              myTask.cancel(true);
              //finish();
          }
    });

Ainsi, lorsque j'appuie sur le bouton Précédent, l'OnCancelListener ci-dessus annule le dialogue et la tâche. Vous pouvez aussi ajouter finish () si vous souhaitez terminer l’activité complète au dos. N'oubliez pas de déclarer votre tâche asynchrone comme une variable comme celle-ci:

    MyAsyncTask myTask=null;

et exécutez votre tâche asynchrone comme ceci:

    myTask = new MyAsyncTask();
    myTask.execute();
32
MScott

Du SDK:

Annuler une tâche

Une tâche peut être annulée à tout moment en appelant cancel (booléen). L'appel de cette méthode fera que les appels suivants à isCancelled () renverront true.

Après avoir appelé cette méthode, onCancelled (Object), au lieu de onPostExecute (Object), sera invoqué après le retour de doInBackground (Object []).

Pour que la tâche soit annulée le plus rapidement possible, vous devez toujours vérifier périodiquement la valeur de retour de isCancelled () dans doInBackground (Object []), si possible (à l'intérieur d'une boucle, par exemple).

Donc, votre code est correct pour l'écouteur de dialogue:

uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
    public void onCancel(DialogInterface dialog) {
        myTask.cancel(true);
        //finish();
    }
});

Maintenant, comme je l'ai mentionné plus tôt dans le Kit de développement logiciel (SDK), vous devez vérifier si la tâche est annulée ou non. Pour cela, vous devez vérifier isCancelled () dans la méthode onPreExecute ().

Par exemple:

if (isCancelled()) 
    break;
else
{
   // do your work here
}
101
Paresh Mayani

J'ai passé un certain temps à comprendre cela, tout ce que je voulais, c'était un exemple simple de la façon de le faire. J'ai donc pensé poster ce que j'ai fait. Il s'agit d'un code qui met à jour une bibliothèque et contient une boîte de dialogue indiquant le nombre de livres mis à jour et annulant lorsqu'un utilisateur ferme la boîte de dialogue:

private class UpdateLibrary extends AsyncTask<Void, Integer, Boolean>{
    private ProgressDialog dialog = new ProgressDialog(Library.this);
    private int total = Library.instance.appState.getAvailableText().length;
    private int count = 0;

    //Used as handler to cancel task if back button is pressed
    private AsyncTask<Void, Integer, Boolean> updateTask = null;

    @Override
    protected void onPreExecute(){
        updateTask = this;
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        dialog.setOnDismissListener(new OnDismissListener() {               
            @Override
            public void onDismiss(DialogInterface dialog) {
                updateTask.cancel(true);
            }
        });
        dialog.setMessage("Updating Library...");
        dialog.setMax(total);
        dialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... arg0) {
            for (int i = 0; i < appState.getAvailableText().length;i++){
                if(isCancelled()){
                    break;
                }
                //Do your updating stuff here
            }
        }

    @Override
    protected void onProgressUpdate(Integer... progress){
        count += progress[0];
        dialog.setProgress(count);
    }

    @Override
    protected void onPostExecute(Boolean finished){
        dialog.dismiss();
        if (finished)
            DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY, Str.TEXT_UPDATECOMPLETED, Library.instance);
        else 
            DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY,Str.TEXT_NOUPDATE , Library.instance);
    }
}
10
odiggity

créer des variables de membre dans votre activité comme

YourAsyncTask mTask;
Dialog mDialog;

utilisez-les pour votre dialogue et votre tâche;

dans onPause () simplement appeler

if(mTask!=null) mTask.cancel(); 
if(mDialog!=null) mDialog.dismiss();
9
jkhouw1

Je voudrais améliorer le code. Lorsque vous pouvez appeler le aSyncTask, la onCancelled() (méthode de rappel de aSyncTask) est automatiquement appelée et vous pouvez y masquer votre progressBarDialog.

Vous pouvez également inclure ce code:

public class information extends AsyncTask<String, String, String>
    {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(String... arg0) {
            return null;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            this.cancel(true);
        }

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

        @Override
        protected void onCancelled() {
            Toast.makeText(getApplicationContext(), "asynctack cancelled.....", Toast.LENGTH_SHORT).show();
            dialog.hide(); /*hide the progressbar dialog here...*/
            super.onCancelled();
        }

    }
5
Rahul Raina

La plupart du temps, j'utilise AsyncTask, ma logique métier se trouve sur une classe métier séparée plutôt que sur l'interface utilisateur. Dans ce cas, je ne pouvais pas avoir de boucle à doInBackground (). Un exemple serait un processus de synchronisation qui consomme des services et conserve les données les unes après les autres.

Je finis par confier ma tâche à l'objet métier afin qu'il puisse gérer les annulations. Ma configuration est la suivante:

public abstract class MyActivity extends Activity {

    private Task mTask;
    private Business mBusiness;

    public void startTask() {
        if (mTask != null) {
            mTask.cancel(true);
        }
        mTask = new mTask();
        mTask.execute();
    }
}

protected class Task extends AsyncTask<Void, Void, Boolean> {
    @Override
    protected void onCancelled() {
        super.onCancelled();

        mTask.cancel(true);

        // ask if user wants to try again
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        return mBusiness.synchronize(this);
    }

    @Override
    protected void onPostExecute(Boolean result) {
        super.onPostExecute(result);

        mTask = null;

        if (result) {
            // done!
        }
        else {
            // ask if user wants to try again
        }
    }
}

public class Business {
    public boolean synchronize(AsyncTask<?, ?, ?> task) {
        boolean response = false;
        response = loadStuff(task);

        if (response)
            response = loadMoreStuff(task);

        return response;
    }

    private boolean loadStuff(AsyncTask<?, ?, ?> task) {
        if (task != null && task.isCancelled()) return false;

        // load stuff

        return true;
    }
}
3
saulobrito

Comment annuler AsyncTask

La réponse complète est ici - Exemple d'AsyncTask Android

AsyncTask fournit une meilleure stratégie d'annulation pour mettre fin à la tâche en cours d'exécution.

cancel (booléen mayInterruptIfitRunning)

myTask.cancel (false) - Fait que isCancelled renvoie true. Aide à annuler la tâche.

myTask.cancel (true) - Cela permet également à isCancelled () de renvoyer true, d’interrompre le fil d’arrière-plan et de libérer les ressources.

Elle est considérée comme un moyen arrogant. Si une méthode thread.sleep () exécute un thread d'arrière-plan, cancel (true) interrompra le thread d'arrière-plan à ce moment-là. Mais cancel (false) l'attendra et annulera la tâche lorsque cette méthode sera terminée.

Si vous appelez cancel () et que doInBackground () n’a pas encore commencé, exécutez-le. onCancelled () invoquera.

Après avoir appelé cancel (…), vérifiez régulièrement la valeur renvoyée par isCancelled () sur doInbackground (). comme ci-dessous.

protected Object doInBackground(Params… params)  { 
 while (condition)
{
 ...
if (isCancelled()) 
break;
}
return null; 
}
2
Thomas Daniel

J'ai eu un problème similaire - en gros, je recevais un NPE dans une tâche asynchrone après que l'utilisateur ait détruit l'activité. Après avoir étudié le problème du débordement de pile, j'ai adopté la solution suivante:

volatile boolean running;

public void onActivityCreated (Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    running=true;
    ...
    }


public void onDestroy() {
    super.onDestroy();

    running=false;
    ...
}

Ensuite, je vérifie "si je cours" périodiquement dans mon code asynchrone. J'ai déjà testé cela et je suis maintenant incapable de "casser" mon activité. Cela fonctionne parfaitement et a l'avantage d'être plus simple que certaines des solutions que j'ai vues sur SO.

2
IanB

Vous pouvez simplement demander une annulation mais pas vraiment y mettre fin. Voir ceci réponse .

2
advantej