Android-Annuler AsyncTask Avec Force

j'ai mis en œuvre AsyncTask dans celui de mon activité:

 performBackgroundTask asyncTask = new performBackgroundTask();
 asyncTask.execute();

maintenant, je dois implémenter la fonctionnalité du bouton" Cancel", donc je dois arrêter l'exécution de la tâche en cours. Je ne sais pas comment arrêter la tâche de course(tâche de fond).

alors s'il vous plaît suggérez-moi, Comment puis-je annuler l'AsyncTask avec force ?

mise à jour:

j'ai trouvé à propos de la méthode Cancel() de la même, mais je trouvé que l'appel cancel(boolean mayInterruptIfRunning) n'arrête pas nécessairement l'exécution du processus de fond. Tout ce qui semble se produire est que L'AsyncTask exécutera oncanceled(), et ne lancera pas onPostExecute() lorsqu'il sera terminé.

57
demandé sur Paresh Mayani 2011-01-20 18:26:47

6 réponses

il suffit de vérifier isCancelled() de temps en temps:

 protected Object doInBackground(Object... x) {
    while (/* condition */) {
      // work...
      if (isCancelled()) break;
    }
    return null;
 }
85
répondu Jacob Nordfalk 2014-05-26 04:40:23

Appel cancel() sur le AsyncTask . La question de savoir si cela va effectivement annuler quoi que ce soit dépend un peu de ce que vous faites. Pour citer Romain Guy:

si vous appelez cancel (true) , une interruption sera envoyé pour le thread d'arrière-plan, ce qui peut aider les tâches interruptibles. Sinon, vous devriez simplement vous assurer que pour vérifier régulièrement isCancelled() dans votre méthode doInBackground (). Vous pouvez voir des exemples de ce à code.google.com/p/shelves.

49
répondu CommonsWare 2011-01-20 15:31:03

Cela dépend vraiment de ce que vous faites dans votre asynctask.

si c'est une boucle qui traite beaucoup de fichiers, vous pouvez juste vérifier après chaque fichier si le drapeau isCanceled() est levé ou non et puis casser de votre boucle si c'est le cas.

si c'est une commande en une seule ligne qui exécute une opération très longue, il n'y a pas grand chose que vous puissiez faire.

La meilleure solution serait de ne pas utiliser la méthode d'annulation de l'asynctask et utiliser votre propre cancelFlag boolean. Vous pouvez ensuite tester ce paquet cancelFlag dans votre postexécute pour décider ce qu'il faut faire avec le résultat.

15
répondu Yahel 2015-03-24 12:22:08

le cas mentionné dans les commentaires que isCancelled() always returns false even i call asynctask.cancel(true); est particulièrement nuisible si je ferme mon application, mais L'AsyncTask continue à travailler.

pour résoudre ce problème, j'ai modifié le code proposé par Jacob Nordfalk de la manière suivante:

protected Object doInBackground(Object... x) {
    while (/* condition */) {
      // work...
      if (isCancelled() || (FlagCancelled == true)) break;
    }
    return null;
 }

et a ajouté ce qui suit à l'activité principale:

@Override
protected void onStop() {
    FlagCancelled = true;
    super.onStop();
}

comme mon AsyncTask était une classe privée de l'un des points de vue, donc getters ou setters du drapeau ont été nécessaires pour informez L'AsyncTask de la valeur actuelle du drapeau.

mes tests multiples (AVD Android 4.2.2, Api 17) ont montré que si un AsyncTask exécute déjà son doInBackground , alors isCancelled() ne réagit en aucune façon (c'est-à-dire continue à être faux) à toute tentative de l'annuler, par exemple pendant mViewGroup.removeAllViews(); ou pendant OnDestroy du MainActivity , dont chacun conduit à la désactivation des vues

   @Override 
   protected  void  onDetachedFromWindow() { 
    mAsyncTask.cancel(false); // and the same result with mAsyncTask.cancel(true);
    super.onDetachedFromWindow(); 
   } 

si je réussis à forcer l'arrêt doInBackground() grâce à l'introduit FlagCancelled , puis onPostExecute() est appelé, mais ni onCancelled() ni onCancelled(Void result) (depuis API Niveau 11) ne sont invoqués. (Je n'ai aucune idée pourquoi, parce qu'ils devraient être invoqués et onPostExecute() ne devrait pas, "dit L'API doc D'Android:appeler la méthode cancel () garantit que onPostExecute (Object) n'est jamais invoqué."- IdleSun , répondant à une question similaire ).

d'autre part, si le même AsyncTask n'avait pas commencé son doInBackground() avant d'annuler, alors tout va bien, isCancelled() change à true et je peux vérifier que dans

@Override
    protected void onCancelled() {
        Log.d(TAG, String.format("mAsyncTask - onCancelled: isCancelled = %b, FlagCancelled = %b", this.isCancelled(), FlagCancelled ));
    super.onCancelled();
}
5
répondu Elia12345 2017-05-23 11:47:29

même si un AsyncTask ne doit pas être utilisé pour des opérations de longue durée, il peut parfois être pris dans une tâche qui ne répond pas (comme un appel HTTP non-réponse). Dans ce cas, il peut être nécessaire d'annuler l'AsyncTask.

Nous avons à relever des défis dans ce domaine. 1. Le dialogue de progression habituel affiché avec un AsyncTask est la première chose annulée sur un AsyncTask lorsque le bouton back est pressé par l'utilisateur. 2. L'AsyncTask peut-être dans le doInBackground method

en créant un dismissDialogListerner sur le ProgressDialog, un utilisateur peut appuyer sur le bouton arrière et en fait annuler le AsycnTask et fermer le dialogue lui-même.

voici un exemple:

public void openMainLobbyDoor(String username, String password){
    if(mOpenDoorAsyncTask == null){
        mOpenDoorAsyncTask = (OpenMainDoor) new OpenMainDoor(username, password, Posts.API_URL, 
                mContext, "Please wait while I unlock the front door for you!").execute(null, null, null);
    }
}

private class OpenMainDoor extends AsyncTask<Void, Void, Void>{

    //declare needed variables
    String username, password, url, loadingMessage;
    int userValidated;
    boolean canConfigure;
    Context context;
    ProgressDialog progressDialog;

    public OpenMainDoor(String username, String password, String url, 
                Context context, String loadingMessage){
        userValidated = 0;
        this.username = username;
        this.password = password;
        this.url = url;
        this.context = context;
        this.loadingMessage = loadingMessage;
    }

    /**
     * used to cancel dialog on configuration changes
     * @param canConfigure
     */
    public void canConfigureDialog(boolean canConfigure){
        this.canConfigure = canConfigure;
    }

    @Override
    protected void onPreExecute(){
        progressDialog = new ProgressDialog(this.context);
        progressDialog.setMessage(loadingMessage);
        progressDialog.setIndeterminate(true);
        progressDialog.setCancelable(true);
        progressDialog.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                mOpenDoorAsyncTask.cancel(true);
            }
        });
        progressDialog.show();
        this.canConfigure = true;
    }

    @Override
    protected Void doInBackground(Void... params) {
        userValidated = Posts.authenticateNTLMUserLogin(username, password, url, context);
        while(userValidated == 0){
            if(isCancelled()){
                break;
            }
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void unused){
        //determine if this is still attached to window
        if(canConfigure)
            progressDialog.dismiss();

        if(userValidated == 1){
            saveLoginValues(username, password, true);
            Toast.makeText(context, R.string.main_login_pass, Toast.LENGTH_SHORT).show();
        }else{
            saveLoginValues(username, password, false);
            Toast.makeText(context, R.string.main_login_fail, Toast.LENGTH_SHORT).show();
        }
        nullifyAsyncTask();
    }

    @Override
    protected void onCancelled(){
        Toast.makeText(context, "Open door request cancelled!", Toast.LENGTH_SHORT).show();
        nullifyAsyncTask();
    }
}
2
répondu Droid Chris 2014-09-23 21:48:31

notre variable de classe AsyncTask globale

LongOperation LongOperationOdeme = new LongOperation();

et action KEYCODE_BACK qui interrompt AsyncTask

   @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            LongOperationOdeme.cancel(true);
        }
        return super.onKeyDown(keyCode, event);
    }

ça me va.

1
répondu Göksel Güren 2013-02-11 10:15:16