web-dev-qa-db-fra.com

Android - Annuler de force la tâche asynchrone

J'ai implémenté AsyncTask dans mon activité:

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

Maintenant, je dois implémenter la fonctionnalité du bouton "Annuler", je dois donc arrêter l'exécution de la tâche en cours. Je ne sais pas comment arrêter la tâche en cours (tâche en arrière-plan).

Alors, s'il vous plaît, suggérez-moi, comment puis-je annuler la AsyncTask avec force?

Mise à jour:

J'ai trouvé à propos de la méthode Cancel() de la même chose, mais j'ai trouvé que l'appel cancel(boolean mayInterruptIfRunning) n'arrête pas nécessairement l'exécution du processus en arrière-plan. Tout ce qui semble se produire est que la AsyncTask exécutera onCancelled () et ne s'exécutera pas onPostExecute () à la fin.

62
Paresh Mayani

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

 protected Object doInBackground(Object... x) {
    while (/* condition */) {
      // work...
      if (isCancelled()) break;
    }
    return null;
 }
90
Jacob Nordfalk

Appelez cancel() sur le AsyncTask. Que cela annule ou non quelque chose dépend en quelque sorte de ce que vous faites. Pour citer Romain Guy:

Si vous appelez cancel (true), une interruption sera envoyée au thread en arrière-plan, ce qui peut faciliter les tâches pouvant être interrompues. Sinon, vous devez simplement vous assurer de vérifier régulièrement isCancelled () dans votre méthode doInBackground (). Vous en trouverez des exemples à l'adresse code.google.com/p/shelves.

49
CommonsWare

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

S'il s'agit d'une boucle traitant beaucoup de fichiers, vous pouvez simplement vérifier après chaque fichier si l'indicateur isCanceled () est levé ou non, puis interrompre votre boucle si c'est le cas.

Si c'est une commande d'une ligne qui effectue une très longue opération, vous ne pouvez pas faire grand chose.

La meilleure solution de contournement serait de ne pas utiliser la méthode cancel de l'asynctask et d'utiliser votre propre booléen cancelFlag. Vous pouvez ensuite tester cet cancelFlag dans votre postExecute pour décider quoi faire du résultat.

16
Yahel

La mention dans les commentaires cas que isCancelled() always returns false even i call asynctask.cancel(true); est particulièrement nocif si je ferme mon application, mais le AsyncTask continue de fonctionner.

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 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, les getters ou les setters du drapeau étaient nécessaires pour informer le AsyncTask de la valeur actuelle du drapeau.

Mes multiples tests (AVD Android 4.2.2, Api 17) ont montré que si une AsyncTask exécute déjà sa doInBackground, alors isCancelled() ne réagit pas manière (c’est-à-dire continue à être fausse) d’essayer de l’annuler, p.ex. pendant mViewGroup.removeAllViews(); ou pendant un OnDestroy du MainActivity, qui conduisent chacun à un détachement 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 de la doInBackground() grâce au FlagCancelled introduit, alors onPostExecute() est appelée, mais ni onCancelled() ni onCancelled(Void result) (à partir du niveau d’API 11) ne sont pas appelés. (Je ne sais pas pourquoi, car ils devraient être invoqués et que onPostExecute() ne devrait pas ", explique l'API pour l'API Android: l'appel de la méthode cancel () garantit que onPostExecute (Object) n'est jamais appelé." - IdleSun, répondant à une question similaire ).

Par contre, si le même AsyncTask n’avait pas commencé sa doInBackground() avant d’annuler, tout est ok, isCancelled() devient true et je peux vérifier cela dans

@Override
    protected void onCancelled() {
        Log.d(TAG, String.format("mAsyncTask - onCancelled: isCancelled = %b, FlagCancelled = %b", this.isCancelled(), FlagCancelled ));
    super.onCancelled();
}
5
Elia12345

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 sans réponse). Dans ce cas, il peut être nécessaire d'annuler la tâche Async.

Pour ce faire, nous devons relever des défis. 1. La boîte de dialogue de progression habituelle affichée avec une AsyncTask est la première chose annulée sur une AsyncTask lorsque l'utilisateur clique sur le bouton Précédent. 2. AsyncTask peut être dans la méthode doInBackground

En créant un licenciementDedogDialog sur ProgressDialog, un utilisateur peut appuyer sur le bouton Précédent pour annuler la tâche AsycnTask et fermer la boîte de dialogue elle-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
Droid Chris

Notre variable de classe AsyncTask globale

LongOperation LongOperationOdeme = new LongOperation();

Et l'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 marche pour moi.

1
Göksel Güren