Qué son los Hilos en Android
Todo programador de aplicaciones Android, en algún momento necesita conocer sobre el manejo de hilos en Android, y gestionarlos dentro de su aplicación.
A continuación te mostramos una pantalla común en muchas aplicaciones, y a veces nos preguntamos ¿por qué ocurre esto?
¿Qué son los Hilos en Android?
Una aplicación Android se ejecuta siempre en un proceso del sistema operativo, todos los componentes de la App (por defecto) se ejecutan dentro de ese mismo proceso.
Una aplicación Android crea siempre un subproceso o un hilo principal de ejecución. Todo el código de nuestra aplicación se ejecuta dentro de este subproceso, esto incluye dibujar y actualizar la interfaz de usuario.
El hilo principal es el encargado de enviar los eventos a los widgets apropiados de la interfaz de usuario, así como la comunicación entre cada uno de los componentes.
Para mantener la respuesta de la aplicación, es esencial evitar el uso del hilo principal para realizar cualquier operación que pueda terminar bloqueándola.
Procesos y subprocesos en Android
¿Qué sucede si se ejecuta una operación muy pesada, de muy larga duración dentro del hilo principal?: La aplicación se bloquea y no responde a la interacción del usuario (error ANR «aplicación no responde»), y se muestra un mensaje similar al que vimos en la imagen anterior.
Para evitar estos errores, seguimos dos reglas:
- No bloquear el hilo principal.
- Todas las operaciones de interfaz gráfica se hacen en el hilo principal.
Android ofrece muchas maneras para crear y administrar hilos, y existen librerías de terceros que hacen que la gestión de hilos en Android sea mucha mas sencilla y agradable para el programador.
Las operaciones de red, y las llamadas a la base de datos, servicios web y la carga de algunos componentes (Ej. imágenes de internet), son ejemplos comunes de operaciones que uno como programador debe evitar hacerlo en el hilo principal.
En Android, se puede categorizar todos los componentes de subprocesamiento en dos categorías:
- Subprocesos que estan adjuntos a una actividad / fragmento: estos subprocesos están vinculados al ciclo de vida de la actividad / fragmento y finalizan tan pronto como se destruya la actividad /fragmento.
- Subprocesos que no estan adjuntos a ninguna actividad / fragmento: estos subprocesos pueden seguir ejecutandose más allá de la duración de la actividad / fragmento (si corresponde ) de la que se generarón.
Android permite crear hilos o subprocesos adicionales, para ejecutar tareas de larga duración. Una vez terminada la tarea podemos regresar al hilo principal para actualizar la interfaz de usuario.
Para realizar la implementación del gráfico anterior hay que seguir los siguientes pasos muy básicos:
- Extender la clase Thread.
- Implementar el médoto run().
- Para actualizar la interfaz de usuario, utilizar el método runOnUiThread().
A continuación para probar este proceso, agregamos un botón a nuestra actividad, colocamos el siguiente código sencillo que explica el proceso anterior:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { Thread.sleep(10000); Toast.makeText(MainActivity.this, "Proceso terminado", Toast.LENGTH_SHORT).show(); } catch (InterruptedException e) { e.printStackTrace(); } } }); }
Al ejecutar la aplicación, y hacemos click en el botón veremos que la aplicación queda bloqueada por unos 10 segundos, ya que simulamos ese proceso y no permite realizar otra tarea.
Ahora vamos a realizar algunos cambios al ejemplo anterior, agregamos un Progressbar justo debajo del botón:
<ProgressBar android:id="@+id/progressBar1" style="?android:attr/progressBarStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminate="true" android:visibility="invisible" tools:layout_editor_absoluteX="167dp" android:layout_marginTop="16dp" android:layout_below="@id/button1" android:layout_centerHorizontal="true"/>
Y cambiamos el código anterior por este nuevo código:
Button button1; ProgressBar progressBar1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progressBar1 = (ProgressBar) findViewById(R.id.progressBar1); button1 = (Button) findViewById(R.id.button1); button1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { progressBar1.setVisibility(View.VISIBLE); new Hilo1().start(); } }); } class Hilo1 extends Thread { @Override public void run() { try { Thread.sleep(5000); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "Proceso terminado", Toast.LENGTH_SHORT).show(); progressBar1.setVisibility(View.INVISIBLE); } }); } catch (InterruptedException e) { e.printStackTrace(); } } }
Al ejecutar la aplicación notaremos que el subproceso al que llama el botón se ejecuta en un subproceso secundario, y no afecta al hilo principal de la aplicación.
Uso de AsyncTask para manejo de hilos en Android
Es el componente más utilizado en Android para crear hilos. Es simple de utilizar y es muy bueno para escenarios básicos.
Permite implementar tareas en paralelo. Solo es necesario extender la clase AsynTask e implementar el método doInBAckground.
Provee otros métodos para actualizar la UI antes, durante y después de la operación en el hilo. Automáticamente ejecuta los procesos en el hilo correcto.
En el ejemplo siguiente vamos a utilizar AsyncTask para realizar la simulación de un inicio de sesión, el mismo que envía los datos del usuario (login) a una segunda actividad (ResutadoActivity):
El código de la actividad sería el siguiente:
public class MainActivity extends AppCompatActivity { Button btnIniciarSesion; EditText txtUsuario, txtPassword; ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnIniciarSesion = (Button) findViewById(R.id.btnIniciarSesion); txtUsuario = (EditText) findViewById(R.id.txtUsuario); txtPassword = (EditText) findViewById(R.id.txtPassword); progressBar = (ProgressBar) findViewById(R.id.progressBar); btnIniciarSesion.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { new Task1().execute(txtUsuario.getText().toString()); } }); } class Task1 extends AsynTask<String, Void, String>{ @Override protected void onPressExceute(){ progressBar.setVisibility(View.VISIBLE); btnIniciarSesion.setEnabled(false); } @Override protected String doInBackground(String... strings){ try{ Thread.sleep(5000); }catch(InterruptedException e){ e.printStackTrace(); } return strings[0]; } @Override protected void onPostExecute(String s){ progressBar.setVisibility(VIEW.INVISIBLE); btnIniciarSesion.setEnable(true); Intent intent = new Intent(MainActivity.this, ResultadoActivity.class); intent.putExtra("usuario",txtUsuario.getText().toString()); startActivity(intent) } } }
En el ejemplo anterior podemos notar el uso de los métodos onPressExecute (antes de la ejecución del subproceso), el método doInBackground (ejecución del subproceso), y el método onPostExecute (después de la ejecución del subproceso), el mism que es realizado automáticamente por la misma clase AsyncTask.
AyncTask sin embargo se queda corto si necesita que su tarea se ejecute más allá de la duración de la actividad / fragmento. Vale la pena señalar que a veces algo tan simple como cambiar de pantalla puede matar la actividad y por consiguiente el subproceso.
Para controlar el problema mencionado, es mejor utilizar cargadores o loaders, estos pueden deternerse cuando la actividad se destruye, e iniciarse después de que se crea la Actividad, para ello manejamos la clase AyncTaskLoader y CursorLoader, pero eso es materia de otro artículo.
Palabras finales.
Como hemos podido apreciar, Android nos brinda la solución para evitar que el hilo principal se paralice, es tema de crear un subproceso y aplicarlo en el momento oportuno,. Si te gusto el articulo, no olvides compartir y seguirnos en nuestras redes sociales.