lunes, 11 de junio de 2012

Threads en Android I: Messages

A continuacion, les voy a compartir 3 excelentes entradas para aprender a trabajar con Threads en Android. Esta informacion fue obtenida desde el sitio web: http://androideity.com , con una excelente explicacion, y por eso se los comparto a ustedes.
SAludos, y a continuacion, en 3 dias seguidos, los post's sobre Threads.

Trabajando con Threads en Android I: Messages

 |  | 14 Comentarios
En un post anterior platicábamos acerca de las generalidades del multitasking en Android. Lo que debes tener muy en claro es que las aplicaciones que consultan datos, manejan archivos y se conectan a Internet consumen muchos recursos y sobre todo tiempo. Dado que los usuarios son  seres muy desesperados, los desarrolladores tenemos que idear “trucos” para que nuestras aplicaciones no parezcan lentas sin importar qué tipo de tareas estén realizando.
Responder a una solicitud de un usuario de forma rápida (por ejemplo, en 200 milisegundos) es un buen objetivo. Debes considerar también que las aplicaciones tienen 5 segundos para responder a una acción del usuario; de lo contrario es muy probable que el ActivityManager determine que la aplicación no está respondiendo y proceda a matarla.
Habrá ocasiones en las que la aplicación deba realizar tareas que tomen una cantidad indeterminada de tiempo. Es en estos escenarios en los cuáles debemos hacer uso de subprocesos que corran en el background (o comúnmente denominado segundo plano). Android nos proporciona varias formas de implementarlo.
Aquí también entra en juego el concepto de “interacción segura”, que se refiere a que no se pueden hacer cambios sobre la interfaz de usuario desde un proceso en segundo plano de forma directa. El UI thread se refiere al proceso principal que es dónde se está ejecutando la actividad y con ella, todos los elementos visuales que esto conlleva. Por ello, es necesario implementar una forma de comunicar cuando los procesos en segundo plano hayan concluido sus tareas y la interfaz de la actividad muestre los resultados al usuario.
A continuación vamos a conocer cómo trabajar con threads de forma basica.

Un primer vistazo a los threads utilizando Handlers
El medio más flexible para crear un subproceso en segundo plano de manera muy amigable es haciendo uso de la clase Handler. Bastará con manejar un objeto de tipoHandler por cada actividad para que Android lo agregue al subsistema de ejecución en segundo plano.
Cada thread creado en el background puede comunicarse con el Handler, que realizará todo el trabajo sobre el UI thread de la actividad. Esto es muy importante que lo sepas, ya que todo cambio que se realice en la interfaz de usuario, deberá ocurrir única y exclusivamente en el UI thread.
Ahora, para comunicarnos con el Handler tenemos dos opciones: a través de mensaje y a través de la interfaz Runnable.

Utilizando mensajes
Vamos a realizar un ejemplo para después ir explicando cómo es que funciona el mecanismo de utilizar threads en segundo plano pasando mensajes que vayan modificando el UI thread.
Creamos un nuevo proyecto Android llamado ThreadTest con la versión 2.2.
En esta ocasión lo que vamos a hacer es crear un proceso en segundo plano que actualice el avance de una ProgressBar que definiremos en el UI thread de la aplicación.
Para ello construimos primero la interfaz, modificando el archivo main.xml como te muestro a continuación:
Dentro de un LinearLayout agregamos un ProgressBar que tiene definido un id para poderlo manipular desde el código Java y le asignamos un estilo de los predefinidos en Android que nos permitirá visualizarlo como una barra horizontal que va avanzando de acuerdo al progreso del thread en segundo plano.
Si quieres conocer más acerca de temas y estilos en Android para cambiar el aspecto visual de los widgets que utilices te invito a leer el post “Aplicar estilos y temas en Android”.
Ahora, en la actividad principal escribiremos el siguiente código:
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
 
public class ThreadTestActivity extends Activity {
 ProgressBar bar;
 Handler handler = new Handler(){
  @Override
  public void handleMessage(Message msg){
   bar.incrementProgressBy(5);
  }
 };
 boolean isRunning = false;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bar = (ProgressBar)findViewById(R.id.progress);
    }
 
    public void onStart(){
     super.onStart();
     bar.setProgress(0);
 
     Thread background = new Thread(new Runnable(){
      public void run(){
       try{
        for(int i=0; i<20; i++){
         Thread.sleep(1000);
         handler.sendMessage(handler.obtainMessage());
        }
       }catch(Throwable t){
        //Termina el thread en background
       }
      }
     });
     isRunning = true;
     background.start();
    }
 
    public void stop(){
     super.onStop();
     isRunning = false;
    }
}
Explicamos el código: Abajo de la declaración del objeto ProgressBar, creamos una instancia de Handler con la implementación de su método handleMessage(), que actualizará el ProgressBar en 5 puntos cada vez que reciba un mensaje.
Dentro del método onCreate() recuperamos la referencia de la ProgressBar para poderla utilizar más adelante.
Después tenemos el método onStart() dónde mandamos a llamar a setProgress() en laProgressBar para inicializarla en cero. Inmediatamente creamos el thread que utiliza un objeto anónimo de Runnable implementando el método run() que es en dónde pasará toda la acción. Utilizamos una sentencia for que nos permita dividir el avance de laProgressBar en 20 pasos (en la realidad, el avance de un proceso puede ser incontrolable). Dormiremos el thread durante un segundo, mandamos el mensaje alHandler y repetiremos este proceso 20 veces.
De esta forma podremos ver el avance hasta que llegue a 100, el valor máximo de la barra. En una aplicación más compleja podríamos controlar el valor máximo utilizando el método setMax(). Podríamos por ejemplo, poner como valor máximo el número de filas que estemos procesando de una base de datos, e ir actualizando la ProgressBar con cada fila procesada.
En la línea handler.sendMessage(handler.obtainMessage()); utilizamos una de las versiones existentes del método sendMessage(), sin embargo, existen otras versiones que te pueden ayudar:
  • sendMessage() Coloca el mensaje pasado como parámetro dentro de la cola de forma inmediata.
  • sendMessageAtFrontOfQueue() Coloca el mensaje hasta enfrente de la cola, de manera que nuestro mensaje toma prioridad sobre los demás mensajes.
  • sendMessageAtTime() Coloca el mensaje dentro de la cola en el momento indicado por el segundo parámetro que deberá estar expresado en milisegundos.
  • sendMessageDelayed() Coloca el mensaje dentro de la cola después de todos los mensajes que hayan estado pendientes hasta ese momento. De igual forma, el retardo se define en milisegundos.
Consulta también la lista de métodos de la clase Handler en la documentación oficial.
Ejecutamos el ejemplo y este será el resultado:
En el siguiente post vamos a utilizar la interfaz Runnable para ver su uso en la creación dethreads en segundo plano. ¡No te lo pierdas!.

1 comentario:

  1. Hola. Fijate que en el bucle for tienes un error de tipeo. debe decir: i<20.

    Saludos.
    Andrés.

    ResponderEliminar