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?
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.
Il suffit de vérifier isCancelled()
de temps en temps:
protected Object doInBackground(Object... x) {
while (/* condition */) {
// work...
if (isCancelled()) break;
}
return null;
}
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.
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.
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();
}
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();
}
}
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.