Android: Cancelar Tarea Asíncrona


Uso una tarea asincrónica para cargar una imagen y obtener algunos resultados.

Mientras subo 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 al setCancelable(true).

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

Entonces, ¿cómo puedo arreglar esto? Quiero cancelar tanto el diálogo como la tarea asíncrona cuando presiono el botón atrás. Alguna idea?

EDITAR: ENCONTRADO LA SOLUCIÓN . VER MI RESPUESTA A CONTINUACIÓN.

Author: MScott, 2011-05-18

8 answers

Desde SDK:

Cancelar una tarea

Una tarea puede ser cancelada en cualquier momento invocando cancel(booleano). Invocar este método causará llamadas posteriores a isCancelled() para devolver true.

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

Para garantizar que una tarea se cancele lo antes posible, siempre debe comprobar el valor devuelto de isCancelled () periódicamente desde doInBackground (Object []), si es posible (dentro de un bucle, por ejemplo.)

Así que 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 he mencionado anteriormente desde SDK, tienes que comprobar si la tarea está cancelada o no, para eso tienes que comprobar isCancelled() dentro del método onPreExecute ().

Por ejemplo:

if (isCancelled()) 
    break;
else
{
   // do your work here
}
 93
Author: Paresh Mayani,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-10 08:30:41

ENCONTRÓ LA SOLUCIÓN: He añadido un oyente de acciones antes de subir Dialog.mostrar() así:

    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 tanto el diálogo como la tarea. También puede agregar finish () si desea terminar toda la actividad en la parte posterior presionada. Recuerde declarar su tarea asincrónica como una variable como esta:

    MyAsyncTask myTask=null;

Y ejecuta tu tarea asíncrona así:

    myTask = new MyAsyncTask();
    myTask.execute();
 30
Author: MScott,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-12 00:42:13

Pasé un tiempo averiguando esto, todo lo que quería era un ejemplo simple 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 se cancela cuando un usuario descarta el diálogo:

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
Author: odiggity,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-04 18:37:08

Crea algunas variables miembro en tu actividad como

YourAsyncTask mTask;
Dialog mDialog;

Use estos para su diálogo y tarea;

En onPause() simplemente llame a

if(mTask!=null) mTask.cancel(); 
if(mDialog!=null) mDialog.dismiss();
 9
Author: jkhouw1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-18 09:29:47

Me gustaría mejorar el código. Cuando canel el aSyncTask el onCancelled() (método de devolución de llamada de aSyncTask) se llama automáticamente, y allí puede ocultar su progressBarDialog.

También puedes incluir este código:

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
Author: Rahul Raina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-15 10:28:59

La mayor parte del tiempo que uso AsyncTask mi lógica de negocio es en una clase de negocio 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.

Termino 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<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
Author: saulobrito,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-30 15:32:15

Solo puede solicitar la cancelación, pero en realidad no terminarla. Vea esta respuesta.

 2
Author: advantej,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:14

Tuve un problema similar - esencialmente estaba recibiendo un NPE en una tarea asincrónica después de que el usuario había destruido la actividad. Después de investigar el problema del desbordamiento de pila, 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 asincrónico. He probado el estrés esto y ahora soy incapaz de "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.

 2
Author: IanB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-04 12:32:25