Está en la página 1de 7

Programación concurrente

Por defecto, un proceso sólo comprende un thread, el thread principal. Todos los componentes de la
aplicación se ejecutan en este thread. Para mejorar la experiencia de usuario, Android considera que
una aplicación está bloqueada cuando no responde tras diez segundos. El usuario puede destruirla.

Para evitar tal bloqueo de la aplicación, cualquier procesamiento largo como, por ejemplo, una
descarga web o un cálculo intensivo deben realizarse en un thread secundario dedicado, liberando al
thread principal. Este último puede entonces dedicarse al funcionamiento global de la aplicación y a su
representación gráfica, tarea que se realiza de forma obligatoria en el thread principal.

Es posible crear tantos threads secundarios como se desee. Se recomienda encarecidamente crear
threads antes que procesos, pues estos últimos consumen más recursos.

Si bien no es obligatorio, en muchos casos puede ser oportuno crear servicios para ejecutar los
threads secundarios. En efecto, llegado el caso, el sistema matará de manera prioritaria aquellos
procesos que no muestren nada al usuario o los receptores de eventos sin actividad antes que los
servicios. La clase IntentServicepuede ser otra gran idea en este sentido. En efecto, permite
crear rápidamente servicios que integran un thread secundario, y gestionarlos fácilmente. Es, por
tanto, una solución suplementaria a tener en cuenta además de las que se detallan en este
capítulo.

Es común ejecutar procesamientos largos y querer visualizar sus resultados una vez terminados. O
bien, el procesamiento largo debe realizarse en un thread secundario y su visualización a través la
interfaz de usuario en el thread principal. Es preciso, por tanto, que estos threads puedan comunicarse
mutuamente.

Para ello, existen distintas técnicas a nuestra disposición.

1. AsyncTask
Como su propio nombre indica, la clase As yncTaskpermite realizar una tarea de forma asíncrona.
Es, sin duda, la forma más sencilla de crear y ejecutar un thread secundario y, a continuación, mostrar
el resultado en el thread principal.

Esta clase permite abstraerse de la manipulación de threads, y no tener que preocuparse más que
del procesamiento de fondo a realizar y de la gestión de resultados. La creación del thread
secundario se realiza de forma interna y la visualización de resultados se realiza en el thread
principal. El desarrollador no tiene que preocuparse de la creación del thread secundario, de la
gestión de estos threads ni de la comunicación entre ellos.

La clase A syncTaskproporciona métodos que se invocarán automáticamente en cada etapa de la


tarea: la inicialización, la ejecución del procesamiento, el progreso y la finalización. El desarrollador no
tiene que invocar a estos métodos directamente.

Es preciso, por tanto, crear una clase que herede de la clase AsyncTaske implementar los métodos
deseados. La clase Async Taskrecibe tres tipos genéricos:
Params: tipo de los parámetros pasados a la entrada de la tarea.

Progress: tipo de la unidad de progreso del procesamiento.

Result: tipo del resultado del procesamiento.

Sintaxis

class MiTarea extends AsyncTask<Params, Progress, Result> {


...
}
Ejemplo

public class MiActividadPrincipal extends Activity {


private class NumerosPrimos extends
AsyncTask<Integer, Integer, Integer> {
...
}
}

La declaración de la clase que hereda de la clase As yncTaskse realiza generalmente como clase
privada interna al componente utilizado, como ocurre en nuestro ejemplo.

La primera etapa de la realización de la tarea es su inicialización, que se lleva a cabo implementando


el método on PreExecute. Este método se invoca desde el thread principal y puede, por tanto,
modificar la interfaz de usuario.

Sintaxis

protected void onPreExecute ()

Ejemplo

@Override
protected void onPreExecute() {
super.onPreExecute();
Toast.makeText(MiActividadPrincipal.this,
"¡Cálculo de los números primos iniciado!",
Toast.LENGTH_SHORT)
.show();
}

A continuación se invoca al método doInBackgroundque se encarga de realizar el procesamiento


de la tarea. Este método se invoca desde el thread secundario. Esto permite ejecutar el
procesamiento largo y no bloquear el thread principal. Este método devuelve el resultado del
procesamiento.

Sintaxis

protected abstract Result doInBackground (Params... params)

Ejemplo

@Override
protected Integer doInBackground(Integer... arg0) {
int n = 0;
int nivel=0;
int step = (arg0[1] - arg0[0]) / 10;
for (int i = arg0[0]; i <= arg0[1]; i++) {
if (isPrime(i)) {
n++;
}
if ((i > arg0[0]) && (i % step == 0))
publishProgress(++nivel);
}
return n;
}

El procesamiento contenido en el método doInBackgroundpuede invocar en cualquier momento


al método pu blishProgresspara actualizar la interfaz de usuario según el avance actual del
procesamiento. Esta llamada provocará que se invoque el método onProgressUpdatedesde el
thread principal.
Sintaxis

protected void onProgressUpdate (Progress... values)

Ejemplo

@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
Toast.makeText(MiActividadPrincipal.this,
values[0]+"0% completado", Toast.LENGTH_SHORT).show();
}

Una vez realizado el procesamiento, se invoca el método on PostExecute para procesar el


resultado. Este método se invoca desde el thread principal y puede, por tanto, modificar la interfaz de
usuario.

Sintaxis

protected void onPostExecute (Result result)

Ejemplo

@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
Toast.makeText(MiActividadPrincipal.this,
result + " números primos encontrados en total.",
Toast.LENGTH_SHORT).show();
}

Es posible detener la tarea invocando a su método ca ncel. Para tener en cuenta esta
anulación lo más rápidamente posible, el procesamiento contenido en el
método doInBackground deberá verificar de forma regular el valor de retorno
del método isCan celled. En tal caso, se invocará al método onCancelled en lugar de al
método onPostExecute.

Sólo queda por utilizar esta clase para ejecutar el procesamiento. Para ello, hay que instanciar la
clase e invocar a su método exe cutepara ejecutar la tarea. Este método acepta como entrada uno
o varios parámetros del tipo genérico Params.

Una tarea sólo puede procesarse una única vez. Es preciso crear una nueva instancia para
ejecutar un nuevo procesamiento.

Sintaxis

public final AsyncTask<Params, Progress, Result>


execute (Params... params)
public final boolean cancel (boolean mayInterruptIfRunning)

Ejemplo

public class MiActividadPrincipal extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
NumerosPrimos tarea = new NumerosPrimos();
tarea.execute(0, 10000000);
}
}

En este ejemplo, la tarea se crea y ejecuta desde la creación de la actividad.

2. Thread
Si bien es práctica, la clase AsyncTaskpuede que no nos convenga en ciertos casos. Otra solución
consiste en utilizar la clase Th read. Esta clase permite definir y ejecutar un procesamiento. Es
posible ejecutar varios threads concurrentes y sincronizarlos entre ellos.

La comunicación entre este thread y el thread principal puede realizarse mediante el uso de objetos
de tipo Handler.

a. Creación

Existen dos formas de crear un thread.

La primera forma de crear un thread consiste en instanciar la clase T hread y pasarle como
parámetro un objeto de tipo Runnable, este último permite especificar el procesamiento en su
método r un.
Sintaxis

public Thread (Runnable runnable)

Sintaxis del método run de la clase Runnable

public abstract void run ()

Ejemplo

Thread thread = new Thread(new Runnable() {


@Override
public void run() {
Log.d(TAG, "Ejecución del thread");
}
});

La otra forma de crear un thread consiste en instanciar una clase que herede de la clase Threade
implementando el método r un.
Sintaxis

public void run ()

Ejemplo

public class MiThread extends Thread {


private static final String TAG = "MiThread";

@Override
public void run() {
super.run();
Log.d(TAG, "Ejecución del procesamiento");
}
}

En ambos casos de creación del thread, la ejecución del procesamiento se realiza invocando
almétodo startdel objeto de tipo Thread.
Sintaxis

public synchronized void start ()

Ejemplo

MiThread thread = new MiThread();


thread.start();

b. runOnUIThread

Si un thread secundario quiere ejecutar código sobre el thread principal, por ejemplo para actualizar
directamente la interfaz de usuario, puede hacerlo utilizando el método r unOnUIThread.
Este método recibe como parámetro un objeto de tipo R unnableque contiene el código a ejecutar
en el thread principal. Esto evita tener que implementar un sistema de comunicación entre ambos
threads cuando se trata únicamente de modificar la interfaz de usuario.

Sintaxis

public final void runOnUiThread (Runnable action)

Ejemplo

runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(MiActividadPrincipal.this, "blablabla",
Toast.LENGTH_SHORT)
.show();
}
};

c. Comunicación interthread

La clase Handlerpermite a distintos threads comunicarse entre ellos. En concreto, un objeto de


tipo Handlerpuede enviar mensajes al thread una vez lo ha creado.

La clase Handleres también un medio sencillo para enviar mensajes desde y hacia un
mismo thread. Esto permite planificar el procesamiento de ciertos mensajes o procesamientos
en el tiempo sin tener por qué recurrir a un sistema de alarmas o algún equivalente más
adecuado.

Estos mensajes son bien objetos de tipo Me ssage, o bien objetos de tipo Runnable. Un objeto
de tipo Messagepuede, entre otros, comportar los siguientes datos: el sujeto o tipo de mensaje
mediante un entero, dos enteros y un objeto de tipo Object.
La creación de un objeto de tipo H andler se realiza instanciando directamente a la
clase Handlerutilizando el constructor por defecto. Este handler está asociado al thread que lo ha
creado. Esto provoca que, si la instanciación tiene lugar directamente en la clase de un componente
de aplicación, el thread del handler es el thread principal. En este caso, esto permite a un thread
secundario comunicarse con el thread principal que puede modificar la interfaz de usuario.

La recepción de los mensajes enviados al objeto de tipo Handler se realiza implementando


elmétodo handleMessage. Este método recibe como parámetro el objeto de
tipo Messagerecibido.
Sintaxis

public Handler ()
public void handleMessage (Message msg)

Ejemplo

public class MiActividadPrincipal extends Activity {


private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PROCESAMIENTO_A:
procesamientoA();
break;
case MSG_PROCESAMIENTO_B:
procesamientoB();
break;
}
}
};
}

La creación de un objeto de tipo Mes sage se realiza indirectamente mediante el objeto de


tipo Handler utilizando uno de sus métodos obtainMessage. Estos métodos permiten
especificar como parámetros todos los datos o una parte de ellos.

Sintaxis

public final Message obtainMessage ()


public final Message obtainMessage (int what)
public final Message obtainMessage (int what, Object obj)
public final Message obtainMessage (int what, int arg1, int arg2)
public final Message obtainMessage (int what, int arg1, int arg2,
Object obj)

Ejemplo

Message msg = handler.obtainMessage(what, obj);

Una vez creado el mensaje, basta con enviarlo al handler.

También en este punto, se proveen varios métodos. El primero s endMessage, envía el mensaje y
lo procesa inmediatamente. El segundo s endMessageAtTime, envía el mensaje y lo procesará en
el momento indicado en milisegundos desde el arranque del sistema. Por último, el tercer
método, se ndMessageDelayed, envía el mensaje y lo procesa tras un tiempo de espera
especificado en milisegundos a partir de la recepción del mensaje.

Sintaxis

public final boolean sendMessage (Message msg)


public boolean sendMessageAtTime (Message msg, long uptimeMillis)
public final boolean sendMessageDelayed (Message msg,
long delayMillis)

Ejemplo

Message msg = handler.obtainMessage(what, obj);


handler.sendMessage(msg);

En este ejemplo, el mensaje lo envía y recibe directamente el método handleMessage del


handler.

Si los mensajes que se quiere enviar son sencillos y sólo contienen el asunto, la
clase Ha ndlerproporciona para ello métodos que tienen en cuenta la creación de estos mensajes.
La variante de los métodos propuestos es del mismo tipo que el de los métodos sendMessage.
Sintaxis

public final boolean sendEmptyMessage (int what)


public final boolean sendEmptyMessageAtTime (int what, long uptimeMillis)
public final boolean sendEmptyMessageDelayed (int what, long delayMillis)

Ejemplo

Message msg = handler.obtainMessage(what, obj);


handler.sendEmptyMessageDelayed(msg, 5000);

En este ejemplo, el mensaje lo recibirá el método handleMessagedel handler en cinco segundos.

Por último, como se ha indicado antes, la clase Handler permite a su vez enviar objetos de
tipo Runnablecomo mensaje. La variante de los métodos propuestos es del mismo tipo que el de
los métodos se ndMessage.
Sintaxis

public final boolean post (Runnable r)


public final boolean postDelayed (Runnable r, long delayMillis)
public final boolean postAtTime (Runnable r, long uptimeMillis)

Ejemplo

handler.post(new Runnable() {
public void run() {
Log.d(TAG, "Código enviado al handler");
Toast.makeText(MiActividadPrincipal.this, "Este código ha
sido enviado al handler para la ejecución en su thread.",
Toast.LENGTH_SHORT).show();
}
});

En este ejemplo, se supone que el handler ha sido creado en el thread principal. Puede, por tanto,
mostrar directamente un mensaje de tipo Toast.

También podría gustarte