Mostrar un cuadro de progreso en un AsyncTask

Si estamos realizando una tarea que puede prolongarse en el tiempo, resulta muy importante mostrar al usuario cuando empieza, su progreso y cuando termina. En el siguiente ejercicio vamos a ver un ejemplo algo más complejo de AsyncTask, donde usaremos la clase ProgressDialog para mostrar la evolución de la tarea.

Ejercicio paso a paso: Uso de un cuadro de progreso en un AsyncTask

 

  1. Siguiendo con el ejercicio anterior, reemplaza la clase MiTarea por el código:


       class MiTarea extends AsyncTask<Integer, Integer, Integer> {

             private ProgressDialog progreso;

                   @Override protected void onPreExecute() {

                    progreso = new ProgressDialog(MainActivity.this);

                    progreso.setProgressStyle(ProgressDialog.
                                                                                              STYLE_HORIZONTAL);

                    progreso.setMessage("Calculando...");

                    progreso.setCancelable(false);

                    progreso.setMax(100);

                    progreso.setProgress(0);

                    progreso.show();

             }

             @Override protected Integer doInBackground(Integer... n) {

                    int res = 1;

                    for (int i = 1; i <= n[0]; i++) {

                           res *= i;

                           SystemClock.sleep(1000);

                           publishProgress(i*100 / n[0]);

                    }

                    return res;

             }

             @Override protected void onProgressUpdate(Integer... porc) {

                    progreso.setProgress(porc[0]);

             }

             @Override protected void onPostExecute(Integer res) {

                    progreso.dismiss();

                    salida.append(res + "\n");

             }

       }
 

En esta nueva versión se han incluido los cuatro métodos principales de AsynTask. El primero en ejecutarse será onPreExecute(), donde creamos un ProgressDialog lo configuramos y lo mostramos. Cuando acabe, se ejecuará doInBackground(). En esta versión no podemos llamar simplemente a factorial(), dado que ahora queremos insertar en el bucle la sentencia publishProgress(i*100/n[0]). Lo que ocasionará una llamada a  onProgressUpdate(), desde donde tendremos acceso al interfaz de usuario y podremos actualizar el ProgressDialog. Finalmente cuando doInBackground() termine se llamará a onPostExecute(), donde destruiremos el ProgressDialog y mostraremos el resultado.

  1. Verifica el resultado obtenido

Ejercicio paso a paso: Cancelando un AsyncTask

Dado que AsyncTask se utiliza en tareas prolongadas, es posible que el usuario no quiera esperar a que termine o que descubramos en medio del proceso que no podemos terminar la tarea. Podemos utilizar el método cancel() cuando ocurra esta circunstancia. Si cancelamos una tarea el método onPostExecute(Resultado) no será llamado, y en su lugar se llamará a onCanceled(). El siguiente ejercicio ilustra su uso:

  1. En el método onPreExecute() del AsyncTask cambia el parámetro de progreso.setCancelable(false) a true.
  2. Añade a continuación de la línea que acbas de modificar:


       progreso.setOnCancelListener(new OnCancelListener() {

           @Override public void onCancel(DialogInterface dialog) {

               MiTarea.this.cancel(true);

           }

       });
 

Con esto conseguimos poner un escuchador de evento al ProgressDialog, para que cuando sea cancelado también cancele el AsyncTask.

  1. Dentro de doInBackground() añade la siguiente condición de finalización en el bucle for:


       for (int i = 1; i <= n[0] && !isCancelled(); i++) {
 

De esta forma no seguiremos realizando la tarea si nos cancelan.

  1. Para terminar añade el siguiente método en la clase AsyncTask:


       @Override protected void onCancelled() {

             salida.append("cancelado\n");

       }
 

  1. Verifica el resultado.

 

El metodo get() de AsyncTask

En caso de que necesites el resultado de esta tarea para poder continuar ejecutando tu programa puedes utilizar el siguiente método:

Resultado r = tarea.get();

Lo que hace es esperar a que termine la tarea y devuelve el resultado obtenido. Aunque parezca muy útil, este método no es recomendable usarlo dado que se bloquea hasta que termine la tarea, y esto es justo lo que queríamos evitar al crear el AsyncTasck. Veamos un ejemplo con el siguiente esquema:

Si el método onClickButton() es asociado a la pulsación de un botón y este se pulsa, has de tener claro que el hilo principal quedará bloqueado hasta que termine la tarea.

El método get() dispone de una sobrecarga alternativa que resulta muy práctica. En ella indicamos dos parámetros, donde fijamos un tiempo máximo de la tarea y en que unidades está este tiempo. Por ejemplo, si escribimos get(4, TimeUnit.SECONDS), pasado 4 segundos se detendrá la tarea y se lanzará una excepción. Usar este método puede resultar interesante cuando no tenemos más remedio que bloquear el hilo del interfaz del usuario hasta que termine una tarea.

Vemos un ejemplo de uso. Si hemos creado un AsyncTask que valida un usuario en nuestro servidor podríamos usar el siguiente método:

 

publicbooleanonLogin(String usuario, contrasena){

       try{

             TareaLogin tarea = newTareaLogin();

             tarea.execute(usuario, contrasena);

             returntarea.get(4, TimeUnit.SECONDS);

       } catch(TimeoutException e) {

             Toast.makeText(contexto, "Tiempo excedido al validar",

                           Toast.LENGTH_LONG).show();

       } catch(CancellationException e) {

             Toast.makeText(contexto, "Error al conectar con servidor",

                           Toast.LENGTH_LONG).show();

       } catch(Exception e) {

             Toast.makeText(contexto, "Error con tarea asíncrona",

                           Toast.LENGTH_LONG).show();

       }

   returnfalse;

 

}

Cuando este método sea invocado has de tener claro que el hilo del interfaz de usuario se va a bloquear hasta que termine la tarea. Sin embargo, en este caso particular no queremos que el usuario realice ninguna acción hasta ser validado. Por lo que no se apreciará falta de interactividad. Además, estamos limitando este bloqueo a un máximo de 4 segundos, impidiendo que aparezca el error “La aplicación no responde”. En caso de producirse cualquier problema será tratado en la sección catch, donde mostraremos al usuario el error que se ha producido. TimeoutException ocurrirá si la tarea tarda más de 4 segundos. CancellationException ocurrirá si el método cancel() es invocado dentro de la tarea, supuestamente si el servidor no responde o si la contraseña no es correcta. Pueden producirse un par de tipos de excepciones más relacionadas con hilos de ejecución. Ambas son capturadas mediante la excepción genérica Exception.  

Al final del capítulo10 se hace una discusión más profunda de este método en el ejercicio “Uso síncrono de AsyncTask para acceso al servicio Web PHP de puntuaciones”.

{jcomments on}