Bienvenidos sean a este post, hoy hablaremos sobre los hilos de ejecucion tambien llamados threads, esto es una modalidad de los S.O modernos permitiendo tener varios «hilos» a la vez para poder ejecutar varias tareas a la vez cada una en un hilo diferente, a su vez cada uno de estos hilos comparten variables, codigos, permisos, etc, cuando trabajamos con varios hilos estos pueden acceder a las variables de forma simultanea pero hay que tener cuidado de que un hilo no modifique el valor de una variable utilizada por otro hilo para evitar esto en Java se utiliza la palabra reservada synchronized pero esto sera visto mas adelante.
Cuando la app es ejecutada en Android esta crea un hilo principal, el cual se encargara de los metodos principales como onCreate(), onDraw(), etc. tambien se encarga de atender de los eventos del usuario en el dispositivo, por cada nuevo componente no se crea un hilo principal sino que es utilizado por el hilo principal creado, lo ideal seria no sobrecargar el hilo porque estoy podria ocasionar que la app no responda de forma adecuada, para evitar esto deberemos crear otro hilo para poder ejecutar este proceso sin interrumpir el hilo principal, para estudiar esto de mejor forma vamos a hacer una app para probarlo.
Abramos Android Studio y seleccionemos Start a new Android Studio project y nos aparecera el siguiente cuadro

Cuando aparezca el siguiente cuadro modificaremos solamente Application name por Hilos y en Company domain pueden usar example.org, o si disponen de un dominio propio pueden usarlo como hago yo, luego el resto debera quedar como aparece, pulsan Next, en el siguiente cuadro les sugiero utilizar unicamente Phone and Tablet y la API mas vieja que tengan disponibles, en general la API 14 es una muy buena opcion, pulsan Next para pasar al siguiente cuadro, en ese deberan seleccionar Empty Activity y pulsar Next, para el ultimo deberan dejar como aparece por defecto, es decir Application Name como MainActivity y Layout name como activity_main y habra dos opciones las cuales deberan quedar tildadas, pulsan Finish y comenzara la generacion de nuestra nueva app. Una vez generada deberemos modificar al archivo activity_main.xml de la siguiente manera:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/entrada"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="numberDecimal"
android:text="5">
<requestFocus />
</EditText>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="calcularOperacion"
android:text="Calcular factorial" />
</LinearLayout>
<TextView
android:id="@+id/salida"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text=" "
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
Como pueden ver aca creamos una simple app para ingresar datos por medio de un EditText, luego un boton para ejecutar nuestra app y una salida para mostrar el resultado, ahora procedamos a modificar la clase MainActivity para poder agregar el metodo faltante y poder probar nuestra app, para ello reemplazaremos el codigo actual con este:
package org.example.hilos;
import android.app.Activity;
import android.content.Intent;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
private EditText entrada;
private TextView salida;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
entrada = (EditText) findViewById(R.id.entrada);
salida = (TextView) findViewById(R.id.salida);
}
public void calcularOperacion(View view){
int n = Integer.parseInt(entrada.getText().toString());
salida.append(n + " ! = ");
int res = factorial(n);
salida.append(res + "\n");
}
public int factorial(int n){
int res = 1;
for(int i=1; i<=n; i++){
res *=i;
SystemClock.sleep(1000);
}
return res;
}
}
Como pueden ver primero creamos dos objetos (entrada y salida) para crear el vinculo entre los elementos de nuestro layout y nuestro codigo, luego en onCreate() si realizaremos la conexion entre el elemento del layout y nuestro codigo por medio de estas dos lineas:
entrada = (EditText) findViewById(R.id.entrada); salida = (TextView) findViewById(R.id.salida);
Luego tendremos el metodo calcularOperacion(), llamado por el boton de nuestro layout, para poder realizar el calculo por medio del siguiente metodo llamado factorial(), y este lo agregara al elemento de salida por medio del metodo append(), en el siguiente metodo (factorial) haremos justamente el calculo del valor ingresado en entrada, basicamente lo que hara es: por ejemplo si ingresamos 5 hara el calculo de 1 * 2 * 3 * 4 * 5 y nos devolvera el resultado 120, vamos a probarlo y deberemos obtener un resultado como se ve a continuacion

Como pueden ver nuestra app funciona perfectamente, ahora por ejemplo si nosotros probaramos con un valor mas grande podria ocurrir el siguiente error

Esto es debido a como hablamos antes al hacer un uso donde requiere de mucho recursos del sistema, Android puede considerar que la app no responde y por ende preguntar si desea esperar a que la app concluya o cerrarla para retomar control del S.O., para evitar esto deberemos crear otro hilo para que el hilo principal no pierda tiempo valioso procesando algo que no necesitamos darle toda nuesta atencion, para ello deberemos utilizar la clase Thread, para esto vamos a agregar el siguiente codigo dentro de nuestra clase MainActivity:
class MiThread extends Thread {
private int n, res;
public MiThread(int n){
this.n = n;
}
@Override
public void run(){
res = factorial(n);
salida.append(res + "\n");
}
}
Esta clase sera nuestro propio hilo derivado de la clase Thread, es decir extendiendo la misma, es simple primero tenemos dos variables de tipo entera (int) llamadas n y res, luego tenemos el constructor donde almacenara el valor enviada a esta clase y por ultimo un metodo llamado run() encargado de calcular el factorial del numero informado, ahora modificaremos el metodo calcularOperacion() de la clase MainActivity de la siguiente forma:
public void calcularOperacion(View view){
int n = Integer.parseInt(entrada.getText().toString());
salida.append(n + " ! = ");
MiThread hilo = new MiThread(n);
hilo.start();
}
En este caso la modificacion son las dos ultimas lineas, vamos a crear un nuevo objeto de la nueva clase y le enviaremos el valor de n por medio del constructor, luego iniciaremos el nuevo hilo por medio del metodo start(), probemos la app y veamos que sucede

Eh, que paso? Esto ocurrio debido a que solo el hilo principal podia trabajar sobre si mismo y no permite a ninguna otra interaccion con el, para evitar esto debemos modificar la siguiente linea en el metodo run():
salida.append(res + "\n");
Con el siguiente metodo:
runOnUiThread(new Runnable() { @Override public void run() { salida.append(res + "\n"); } });
Este metodo le dira al sistema que ejecute esta parte del codigo de nuestro hilo principal, nuestro codigo final quedara de la siguiente forma:
package com.tinchicus.hilos;
import android.app.Activity;
import android.content.Intent;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
private EditText entrada;
private TextView salida;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
entrada = (EditText) findViewById(R.id.entrada);
salida = (TextView) findViewById(R.id.salida);
}
class MiThread extends Thread {
private int n, res;
public MiThread(int n){
this.n = n;
}
@Override
public void run(){
res = factorial(n);
runOnUiThread(new Runnable() {
@Override
public void run() {
salida.append(res + "\n");
}
});
}
}
public void calcularOperacion(View view){
int n = Integer.parseInt(entrada.getText().toString());
salida.append(n + " ! = ");
MiThread hilo = new MiThread(n);
hilo.start();
}
public int factorial(int n){
int res = 1;
for(int i=1; i<=n; i++){
res *=i;
SystemClock.sleep(1000);
}
return res;
}
}
Si probamos nuestra app, funcionara de la siguiente forma
Observen como ahora la app funciono correctamente, pudimos calcular varios factoriales sin colgar a nuestra app, lo contrario a nuestro primer codigo donde «colgabamos» a nuestro hilo principal.
En resumen, hoy hemos visto que son y para que sirven los hilos de ejecucion (threads), hemos visto como una app mal creada puede generar inconvenientes a la hora de ejecutarla pero tambien hemos visto como solucionarlo, espero les haya sido util sigueme en Twitter, Facebook o Google+ para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.