Tutorial y ejemplos de Threading de Android

Este es nuestro tutorial de threading en android. Exploramos lo que es un thread y proporcionamos varias abstracciones para la clase Thread.

¿Qué es un Thread?

Un thread es una ruta de ejecución que existe dentro de un proceso.

Un proceso puede tener uno o más threads.

Un proceso Single-threaded tiene un thread mientras que un proceso multi-threaded tiene más de un thread.

En el proceso single-threaded, tenemos sólo un flujo de ejecución de instrucciones mientras que en el proceso multi-threaded tenemos múltiples conjuntos de instrucciones ejecutadas simultáneamente.

Un proceso multi-threaded tiene partes que se ejecutan simultáneamente y cada parte puede hacer una tarea determinada de forma independiente.

Así que el multi-threading es un mecanismo que nos permite escribir programas de manera que múltiples actividades puedan proceder simultáneamente en el mismo programa.

Y especialmente en el entorno actual de dispositivos multinúcleo, los desarrolladores deben ser capaces de crear líneas de ejecución concurrentes que combinen y agreguen datos de múltiples recursos.

Pero también es importante señalar que, en realidad, un sistema que sólo tiene un núcleo de ejecución puede crear la ilusión de una ejecución concurrente. Lo hace ejecutando los distintos "hilos" de forma intercalada.
Pero lo hace de forma rápida, de tal manera que pensamos que realmente está realizando tareas de forma concurrente.

El "hilo" principal

Normalmente cuando ejecutas tu proyecto, tu proceso de aplicación se iniciará. Primero habrá threads de mantenimiento para el Runtime de Android o la máquina virtual Dalvik.

Además, el sistema Android creará un thread de ejecución llamado main. Este será el thread principal para su aplicación.

Este thread también se llama a veces UI thread``.

Tu aplicación puede tener muchos otros threads, normalmente llamados threads de fondo. Sin embargo, el thread principal es el más importante. Es este thread el responsable de interactuar con los componentes y vistas de Android. Los renderiza y también actualiza sus estados.

Este thread es muy crucial, especialmente dado el hecho de que, como hemos dicho, es donde todos los componentes de Android (Activity, Services, BroadcastReceiver ) se ejecutan por defecto.

Este thread es el responsable de manejar y escuchar los eventos de entrada del usuario. Debido a su importancia, siempre es recomendable mantenerlo activo:

    1. No hacer ningún tipo de tarea que pueda llevar mucho tiempo, como "entrada/salida" (I/O) en este "hilo". Estas tareas pueden bloquear el thread principal durante un tiempo indefinido, por lo que deben ser transferidas a un thread en segundo plano.
  1. No hacer tareas intensivas de CPU en este thread. Si tienes algunos cálculos o tareas caras como la codificación de vídeo, necesitas descargarlas también a un thread de fondo.

Este thread normalmente tiene una instalación adjunta llamada Looper. El Looper mantendrá una Cola de Mensajes. Una MessageQueue es simplemente una cola de mensajes con alguna unidad de trabajo que deben ser ejecutados secuencialmente.

Así que cuando un mensaje está listo para ser procesado en la cola, el Looper Thread sacará ese mensaje de la cola. Ese mensaje será reenviado sincrónicamente (secuencialmente) al manejador de destino. Ese manejador ya está especificado en el mensaje.

Entonces el Handler comenzará su trabajo y lo hará. Cuando termina ese trabajo, el thread de Looper` comienza a procesar el siguiente mensaje disponible en la cola y lo pasa para que también se ejecute.

Puedes ver que este proceso es secuencial. Así que supongamos que nuestro Handler no termina su trabajo rápidamente, el Looper se quedará ahí esperando para procesar otros mensajes pendientes en la cola.

En ese caso el sistema mostrará el diálogo Application Not Responding(ANR). Es posible que ya hayas visto esto en algunas aplicaciones. Significa que la aplicación no está respondiendo a las entradas del usuario. Sin embargo, está ocupada haciendo su trabajo.

El diálogo ANR se mostrará a los usuarios si una aplicación no responde a la entrada del usuario en cinco segundos. El sistema ofrecerá entonces a los usuarios la opción de salir de la aplicación.

Te encontrarás con este tipo de escenario cuando intentes realizar tareas intensivas en tu hilo principal. Esto significa, por ejemplo, que cuando intentas hacer lo siguiente en tu thread principal:

    1. Acceder a la red/servicios web/internet
  1. Acceder a los recursos del sistema de archivos.
  2. Tratar de procesar grandes cantidades de datos o hacer cálculos matemáticos complejos, etc.

La mayor parte del código que escribes como en tus actividades, fragmentos, servicios normalmente se ejecutan en el thread principal por defecto, a menos que crees explícitamente un thread de fondo.

El SDK de Android está basado en un subconjunto del SDK de Java. El SDK de Java se deriva del proyecto Apache Harmony y proporciona acceso a construcciones de concurrencia de bajo nivel como:

  1. java.lang.Thread.
  2. java.lang.Runnable.
  3. Palabras clave synchronized y volatile.

Clase Thread

La clase java.lang.Thread es la construcción más básica utilizada para crear threads.

También es la más utilizada. Esta clase nos crea una nueva línea de ejecución independiente en un programa Java.

Una forma de crear un nuevo thread es simplemente subclasificando o extendiendo la clase java.lang.Thread.

public class MyThread extends Thread {
    public void run() {
        Log.d("Generic", "Our thread is running ...");
    }
}

Entonces podemos hacer nuestra tarea en segundo plano dentro del método run().

Sin embargo ese thread aún no está iniciado. Para que eso ocurra tenemos que instanciar esa clase y arrancar explícitamente nuestro thread:

    MyThread myThread = new MyThread();
    myTread.start();

El método start() reside en la clase Thread. Al invocarlo le dice al sistema que cree un thread dentro del proceso y ejecuta el método
método run(). El método run() se ejecutará automáticamente si invocamos el método start().

Métodos Threading comunes

(a). Método Thread.currentThread()

Este método devolverá el Thread del llamante, es decir, el Thread actual.

(b). Thread.sleep(time)

Este método hará que el thread que envió este mensaje duerma durante el intervalo de tiempo dado (en milisegundos y nanosegundos). La precisión no está garantizada - el Hilo puede dormir más o menos de lo solicitado.

Básicamente, se detiene la ejecución del thread actual durante el
período de tiempo dado.

(c). getContextClassLoader()

Este método devuelve el ClassLoader de contexto para este Thread.

(d). Iniciar()

Este método iniciará el nuevo Thread de ejecución. El método run() del receptor será llamado por el propio Thread del receptor (y no por el Thread que llama a start()).

(e). Tread.getName() y Thread.getId()`

Obtienen el "nombre" y el "ID" respectivamente. Se utilizan principalmente para fines de depuración.

(f) Thread.isAlive()

La función isAlive() comprueba si el thread se está ejecutando actualmente o
ya ha terminado su trabajo.

g) Thread.join()

join() bloqueará el thread actual y esperará hasta que el thread accedido
termine su ejecución o muera.

Cómo mantener la capacidad de respuesta de la aplicación

La mejor manera de mantener la capacidad de respuesta de la aplicación no es evitando hacer operaciones de larga duración. En su lugar, es descargándolas del hilo principal
para que puedan ser manejadas en segundo plano por otro hilo.

El hilo principal puede entonces continuar procesando las actualizaciones de la interfaz de usuario sin problemas y responder de manera oportuna a las interacciones del usuario.

Normalmente hay un conjunto de operaciones típicas que son comunes en muchas aplicaciones y que consumen no sólo mucho tiempo sino también recursos del dispositivo.

Estas incluyen:

  • Acceso y comunicación a través de la red, especialmente Internet.
  • Operaciones de entrada y salida de archivos. Estas ocurren en el sistema de archivos local.
  • Procesamiento de imágenes y vídeos.
  • Cálculos matemáticos complejos.
  • Procesamiento de texto - Tratar de procesar o analizar una gran cantidad de texto.
  • Codificación y decodificación de datos.

Ejemplos Rápidos de Threading.

1. Creando un simple Timer con la clase Thread.

Esta es una clase para mostrar cómo implementar un simple temporizador. No imprime nada y es sólo una clase que muestra cómo implementar esta idea.

import java.lang.Thread;

public class TimerClass extends Thread{

    boolean timeExpired;
    double mTime;
    /** Creates a new instance of TimerClass */
    public TimerClass(double time){
        mTime = time;
    }

    public void run(){

        timeExpired = true;
        mTime = mTime * 1000;
        double startTime = System.currentTimeMillis();
        double stopTime = startTime + mTime;
        while(System.currentTimeMillis() < stopTime && timeExpired == false){
              try {
                Thread.sleep(10);
            } catch (InterruptedException e){ }
        }

        timeExpired = true;

    }

    public boolean getTimeExpired(){
        return true;
    }

    public void cancel(){
        timeExpired = true;
    }

}

2. Clase utilitaria Thread completa y reutilizable

import android.os.Looper;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Threading tools
 * </p>
 */
public class ThreadUtils {

    private final static ExecutorService sThreadPool = Executors.newCachedThreadPool();

    /**
     * Current thread
     */
    public static Thread currentThread() {
        return Thread.currentThread();
    }

    /**
     * Current process ID
     */
    public static long currentThreadId() {
        return Thread.currentThread().getId();
    }

    /**
     * Current process name
     */
    public static String currentThreadName() {
        return Thread.currentThread().getName();
    }

    /**
     * Determine if it is a UI thread
     */
    public static boolean isUiThread() {
        return Looper.myLooper() == Looper.getMainLooper();
    }

    /**
     * Runs on the UI thread
     */
    public static void runOnUiThread(Runnable action) {
        if (action == null) {
            return;
        }
        if (Looper.myLooper() == Looper.getMainLooper()) {
            action.run();
        } else {
            HandlerUtils.uiPost(action);
        }
    }

    /**
     * Runs in the background thread
     */
    public static void runOnBackgroundThread(Runnable action) {
        if(action==null){
            return;
        }
        if (Looper.myLooper() != Looper.getMainLooper()) {
            action.run();
        }else{
            sThreadPool.submit(action);
        }
    }

    /**
     * Run on asynchronous thread
     */
    public static void runOnAsyncThread(Runnable action) {
        if (action == null) {
            return;
        }
        sThreadPool.submit(action);
    }

    /**
     * Runs on the current thread
     */
    public static void runOnPostThread(Runnable action) {
        if(action==null){
            return;
        }
        action.run();
    }

    public static void backgroundToUi ( final Runnable background , final Runnable ui ) {
        runOnBackgroundThread(new Runnable() {
            @Override
            public void run() {
                background.run();
                runOnUiThread ( ui );
            }
        });
    }
}

Categorizado en:

Etiquetado en: