Android: Cancelar la tarea asincrónica

Utilizo una tarea asíncrona para cargar una imagen y obtener algunos resultados.

Al subir la imagen, veo un diálogo de progreso, escrito en el método onPreExecute () como este:

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

Ok, cuando presiono el botón Atrás, obviamente el diálogo desaparece debido a setCancelable (verdadero).

Pero (obviamente) la tarea asincrónica no se detiene.

Entonces, ¿cómo puedo solucionar esto? Quiero cancelar tanto el diálogo como la tarea asincrónica cuando presiono el botón Atrás. ¿Algunas ideas?

EDIT: ENCONTRÓ LA SOLUCIÓN . VEA MI RESPUESTA A CONTINUACIÓN.

De SDK:

Cancelando una tarea

Una tarea puede cancelarse en cualquier momento invocando cancelar (booleano). La invocación de este método provocará que las llamadas posteriores a isCancelled () devuelvan verdadero.

Después de invocar este método, onCancelled (Object), en lugar de onPostExecute (Object) se invocará después de que doInBackground (Object []) regrese.

Para garantizar que una tarea se cancele lo más rápido posible, siempre debe verificar el valor de retorno de isCancelled () periódicamente desde doInBackground (Object []), si es posible (dentro de un bucle, por ejemplo).

Entonces su código es correcto para el oyente de diálogo:

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

Ahora, como mencioné anteriormente de SDK, debe verificar si la tarea está cancelada o no, para eso debe verificar isCancelled () dentro del método onPreExecute ().

Por ejemplo:

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

ENCONTRÉ LA SOLUCIÓN : agregué un oyente de acción antes de subir el archivo Dialog.show () de esta manera:

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

De esa manera, cuando presiono el botón Atrás, el OnCancelListener anterior cancela el diálogo y la tarea. También puede agregar finish () si desea finalizar toda la actividad en la parte posterior presionada. Recuerde declarar su tarea asíncrona como una variable como esta:

  MyAsyncTask myTask=null; 

y ejecuta tu tarea asíncrona de esta manera:

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

Pasé un tiempo descifrando esto, todo lo que quería era un simple ejemplo de cómo hacerlo, así que pensé en publicar cómo lo hice. Este es un código que actualiza una biblioteca y tiene un diálogo de progreso que muestra cuántos libros se han actualizado y cancela cuando un usuario cierra el diálogo:

 private class UpdateLibrary extends AsyncTask{ 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 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); } } 

crea algunas variables miembro en tu actividad como

 YourAsyncTask mTask; Dialog mDialog; 

Úselos para su diálogo y tarea;

en OnPause () simplemente llame

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

Me gustaría mejorar el código. Cuando canaliza a aSyncTask, se llama aSyncTask al onCancelled() (método de callback de aSyncTask ), y allí puede ocultar su progressBarDialog .

Puede incluir este código también:

 public class information extends AsyncTask { @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(); } } 

La mayoría de las veces que utilizo AsyncTask, mi lógica empresarial está en una clase de negocios separada en lugar de estar en la interfaz de usuario. En ese caso, no podría tener un bucle en doInBackground (). Un ejemplo sería un proceso de sincronización que consume servicios y persiste datos uno tras otro.

Terminé entregando mi tarea al objeto de negocio para que pueda manejar la cancelación. Mi configuración es así:

 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 { @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; } } 

Solo puede solicitar la cancelación pero no terminarla realmente. Vea esta respuesta .

Tuve un problema similar: esencialmente estaba obteniendo un NPE en una tarea asíncrona después de que el usuario destruyó la actividad. Después de investigar el problema en Stack Overflow, adopté la siguiente solución:

 volatile boolean running; public void onActivityCreated (Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); running=true; ... } public void onDestroy() { super.onDestroy(); running=false; ... } 

Luego, compruebo “si se está ejecutando” periódicamente en mi código asíncrono. He probado el estrés y ahora no puedo “romper” mi actividad. Esto funciona perfectamente y tiene la ventaja de ser más simple que algunas de las soluciones que he visto en SO.