---
title: AsyncTask en Android
url: https://jarroba.com/asynctask-en-android/
author: Ramón Invarato
date: 2013-09-30
modified: 2026-10-21
language: es
license: CC BY-NC-SA 3.0 (https://creativecommons.org/licenses/by-nc-sa/3.0/) — attribution: "AsyncTask en Android", Ramón Invarato, jarroba.com
categories: Aplicaciones Móviles
series: Multitarea en Android (part 2 of 2)
repository: https://github.com/jarroba/AsyncTask
---

# AsyncTask en Android

> Aprende a programar un AsyncTask en Android desde cero: ciclo de vida, doInBackground y onProgressUpdate, cancelación, genéricos y un ejemplo completo.

En este artículo te mostraremos todos los secretos del AsyncTask de Android para programar con multitarea real. Antes de empezar, comentar que aquí nos centraremos en la programación del AsyncTask desde cero, con un ejemplo real y funcional, entendiendo bien cada parte del mismo, explicado de una manera muy fácil con la que creemos desde Jarroba resolverá todas tus dudas para siempre (y sino pregúntanos, que estamos encantados de echarte una mano). Tanto si no conoces AsyncTask, o ya has trabajado con él y quieres profundizar; es muy recomendable que leas [la primera parte de este artículo sobre la multitarea de Android aquí](https://jarroba.com/multitarea-en-android/), que aunque sea teórico está bastante novelado y con bastantes imágenes para que sea muy ameno y enriquecedor. Como siempre que estamos programando en Android, tengo que indicar que este tutorial asume que entiendes lo mínimo de Android, como son las Activities y su ciclo de vida ([repasa las Activities aquí]( https://jarroba.com/activity-entender-y-usar-una-actividad/)). ¿Ya estamos listos? No hay tiempo para la pausa, pongámonos en marcha 😀

 

 

Para programar la multitarea en Android, se puede usar la clase Thread de toda la vida de Java con los métodos synchronized y ser nosotros quienes controlemos todo. Pero gracias a una clase especial llamada AsyncTask, nos facilitará mucho la vida y será mucho más óptimo, será la que recomendamos. Esta clase actúa de editor y nosotros nos suscribiremos a esta al extender (heredar) de ella. Al hacerlo nos pedirá que sobrescribamos e implementemos ciertos métodos que estarán vacíos.

 

Para entender cómo hacerlo, lo mejor es primero entender el **ciclo de vida de la clase AsyncTask**. Imaginemos una aplicación como la del [“Ejemplo de trabajar con el hilo principal solo para la interfaz gráfica y el resto en segundo plano” del artículo anterior dedicado a la multitarea de Android](https://jarroba.com/multitarea-en-android/). Al pulsar el botón de descargar los datos de Internet, crearíamos el hilo en segundo plano usando AsyncTask. Echa un vistazo rápido a la siguiente imagen y continúa leyendo la explicación.

 

[![Ciclo de vida del Asynktask de Android - www.Jarroba.com](https://jarroba.com/wp-content/uploads/2013/09/Ciclo-de-vida-del-Asynktask-de-Android-www.Jarroba.com_.png)](https://jarroba.com/wp-content/uploads/2013/09/Ciclo-de-vida-del-Asynktask-de-Android-www.Jarroba.com_.png)

 

Al crearse el objeto de la clase AsyncTask se llama, para empezar, a su primer método **onPreExecute()** que se ejecuta sobre el hilo principal. Al terminar este AsyncTask crea un hilo secundario y ejecuta su trabajo dentro de **doInBackground()**. Durante la ejecución en segundo plano podemos realizar llamadas al hilo principal (por ejemplo, para incrementar la barra de carga a medida que se ejecuta el hilo en segundo plano) desde doInBackground(), y con ayuda de publishProgress(), a un método sobrescrito llamado **onProgressUpdate()**. No es obligatorio llamar a publishProgress(), pudiendo no llamarlo nunca con lo que nunca entraremos en onProgressUpdate(), o llamándolo las veces que queramos. Al terminar de ejecutarse doInBackground() se llama de inmediato a **onPostExecute()**, y se da por acabado a este hilo en segundo plano.

 

Un inciso, ¿Te has fijado en la imagen como empieza el nombre de los métodos? ¿Cuáles con do y cuáles con on? Huele a programación orientada a eventos.

 

Te puedes preguntar ¿para qué tantos métodos sobrescritos en el hilo principal? Pues cada uno tiene una función que deberás encontrar tú mismo. De todas maneras ninguno del hilo principal es obligatorio implementarlo (Solo es obligatorio implementar “doInBackground()”, que corre en segundo plano y sin él carece de sentido usar “AsyncTask”). Un ejemplo de uso de los métodos sobrescritos en el hilo principal sería:

 
1. onPreExecute: abrir una ventana emergente con la barra de carga vacía que le indica al usuario que espere un momento.
2. onProgressUpdate: repetir 100 veces para ir dibujando la carga de la barra de la ventana desde 0 a 100.
3. onPostExecute: cerrar la ventana emergente.

 

¿Tenemos que ejecutar el doInBackground() hasta el final siempre? No necesariamente. Podemos cancelar la tarea en segundo plano cuando lo necesitemos, con la llamada al método cancel() (se podría invocar, por ejemplo, en el catch de un try porque algo ha ido mal). Lo único que seremos nosotros quienes decidamos detener la ejecución de doInBackground(). Y si, por ejemplo, dentro de ésta existe un while, tendremos que preguntar en cada iteración si está cancelada con isCancelled(). Al terminar de ejecutar el contenido doInBackground() ya no se ejecutará el método sobrescrito onPostExecute() sino **onCancelled()**.

 

[![Ciclo de vida del Asynktask cancelado de Android - www.Jarroba.com](https://jarroba.com/wp-content/uploads/2013/09/Ciclo-de-vida-del-Asynktask-cancelado-de-Android-www.Jarroba.com_.png)](https://jarroba.com/wp-content/uploads/2013/09/Ciclo-de-vida-del-Asynktask-cancelado-de-Android-www.Jarroba.com_.png)

 

Indicar, que onCancelled() solo funciona bien a partir de Android 3.0 en adelante (en versiones anteriores no entra en este método). Es importante conocerlo y saber manejarlo, pues es una buena práctica para el tratamiento de errores y de diferenciar estados de la aplicación (sobre todo para avisar al usuario con una ventana emergente de que se ha cancelado el proceso, por ejemplo).

 

Lee la siguiente nota, pero no te entretengas mucho en ella, pues todo se esclarecerá al ver un ejemplo funcionar un poco más adelante. De momento fíjate solo en los métodos sobrescritos (los que tienen encima un @Override), para que los compares con las imágenes anteriores. Y que te fijes en que extiende la clase de AsyncTask. Ojo, si ves símbolos o cosas extrañas que no entiendes, tranquilo, con calma y sigue leyendo, que los explicamos unas líneas más abajo.

 

**Nota**: a continuación el snippet de código de cómo crear un AsyncTask, y la primera línea con la creación del objeto para que se ponga en marcha el hilo en segundo plano. Este código no hace nada; es decir, sí compila y ejecuta, pero es una plantilla vacía para que en un futuro la copies y pegues. En cuanto entiendas AsyncTask vas a ser todo un lince, programando hilos a toda velocidad y bien; más todavía con este snippet para crear un AsyncTask de Android (cortesía de Jarroba 🙂 ).

 

```java

new HiloEnSegundoPlano().execute("variable Tipo_empezarBackground", "variable Tipo_empezarBackground", "..."); //Arrancamos el AsyncTask. el método "execute" envía datos directamente a doInBackground()

	private class HiloEnSegundoPlano extends AsyncTask <Tipo_empezarBackground, Tipo_duranteBackground, Tipo_terminarBackground> {

		public HiloEnSegundoPlano(){
			//TODO código del constructor
		}

		@Override
		protected void onPreExecute() {
			//TODO código del onPreExecute (Hilo Principal)
		}

		@Override
		protected Tipo_terminarBackground doInBackground(Tipo_empezarBackground... varEntrarBackground) {
			//TODO código del doInBackground (Hilo en Segundo Plano)

			//...
			publishProgress(valor_Tipo_duranteBackground); //Opcional. Para pasar el valor y llamar a onProgressUpdate()
			//...

			//...
			cancel(true); //Opcional. Para cancelar en vez de ejecutar al terminar onPostExecute(), ejecutar onCancelled(). Comprobar si se ha cancelado con isCancelled()
			//...

			return valor_Tipo_terminarBackground;
		}

		@Override
		protected void onProgressUpdate(Tipo_duranteBackground... varDuranteBackground) {
			//TODO código del onProgressUpdate (Hilo Principal)
		}

		@Override
		protected void onPostExecute(Tipo_terminarBackground varTerminarBackground) {
			//TODO código del onPostExecute (Hilo Principal)
		}

		@Override
		protected void onCancelled (Tipo_terminarBackground varTerminarBackground) {
			//TODO código del onCancelled (Hilo Principal)
		}

	}
```

 

Del anterior código, el único método obligatorio es el doInBackground(), el resto los puedes eliminar si no los vas a usar. Por si no tienes suficiente con este tutorial, tienes toda la información oficial del AsyncTask en: [AsyncTask (Android Developers)](http://developer.android.com/reference/android/os/AsyncTask.html)

 

Y por fin, lo que todo el mundo estaba esperando: el reluciente código funcional de una aplicación de ejemplo completa. Con barra de progreso determinada, hasta cómo se maneja cuando se cancela el doInBackground().

 

Vamos a suponer que queremos descargar unas imágenes de Internet (estas no las descargaremos realmente, sino que simularemos la descarga pausando un poco el hilo secundario), a la vez que el usuario pueda hacer otra cosa, como pulsar un botón y que un mensaje aparezca (esto sin hilos sería imposible, como habrás deducido por [las pistas del anterior artículo](https://jarroba.com/multitarea-en-android/)).

 

[![Ejemplo de aplicacion con AsynkTask de Android - www.Jarroba.com](https://jarroba.com/wp-content/uploads/2013/09/Ejemplo-de-aplicacion-con-AsynkTask-de-Android-www.Jarroba.com_.png)](https://jarroba.com/wp-content/uploads/2013/09/Ejemplo-de-aplicacion-con-AsynkTask-de-Android-www.Jarroba.com_.png)

 

Inmediatamente al terminar la descarga haremos que el texto se pondrá verde. Aparte de cambiar lo que nos cuenta, que si te fijas pone "Hilo PRINCIPAL" todo el rato y esto es porque el texto cambia solo en el hilo principal.

 

[![Ejemplo de carga terminada correctamente con AsynkTask de Android - www.Jarroba.com](https://jarroba.com/wp-content/uploads/2013/09/Ejemplo-de-carga-terminada-correctamente-con-AsynkTask-de-Android-www.Jarroba.com_.png)](https://jarroba.com/wp-content/uploads/2013/09/Ejemplo-de-carga-terminada-correctamente-con-AsynkTask-de-Android-www.Jarroba.com_.png)

 

Al igual que queremos que, si se cumple cierta condición (si ponemos true a una variable que se cancele al pasar de 100 imágenes descargadas), la descarga se interrumpa inmediatamente y el texto aparecerá en rojo.

 

[![Ejemplo de carga cancelada con AsynkTask de Android - www.Jarroba.com](https://jarroba.com/wp-content/uploads/2013/09/Ejemplo-de-carga-cancelada-con-AsynkTask-de-Android-www.Jarroba.com_.png)](https://jarroba.com/wp-content/uploads/2013/09/Ejemplo-de-carga-cancelada-con-AsynkTask-de-Android-www.Jarroba.com_.png)

 

El código XML del **activity_main.**xml no tiene más misterio: una barra de progreso, un texto y un botón.

 

```xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <ProgressBar
        android:id="@+id/progressBar_indicador"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp" />

    <TextView
        android:id="@+id/TextView_mensajesAlUsuario"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/button_probarComoPodemosHacerOtraCosa"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:text="Mientras carga la barra haz click aquí, para probar como podemos hacer varias cosas a la vez sin interrumpir al usuario" />

</LinearLayout>
```

 

Para el resto trabajaremos únicamente sobre **MainActivity.java**. Crearemos una clase que extienda de Activity. La cual empezará con tres variables globales.

 

La que se encargará de decir si cancelamos o no la descarga al pasar de 100 imágenes. Lo pondremos a “true” si queremos ver cómo funciona al cancelarse el doInBackground().

 

```java

private final boolean CANCELAR_SI_MAS_DE_100_IMAGENES = false;
```

 

El tag del log para que veamos por consola lo que va haciendo nuestra aplicación.

 

```java

private final String TAG_LOG = "test";
```

 

Y el TextView que será el que muestre el mensaje que cambia de color.

 

```java

private TextView TV_mensaje;
```

 

Crearemos el onCreate del Activity en el que asociaremos el TextView. Le pondremos funcionalidad al botón para que muestre el Toast. Y crearemos y ejecutaremos nuestro AsyncTask al final, al que le meteremos al constructor si queremos que se cancele.

 

```java

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		TV_mensaje = (TextView) findViewById(R.id.TextView_mensajesAlUsuario);

		Button B_probarHacerDosCosasALaVez = (Button) findViewById(R.id.button_probarComoPodemosHacerOtraCosa);
		B_probarHacerDosCosasALaVez.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				Log.v(TAG_LOG, "...Haciendo otra cosa el usuario sobre el hilo PRINCIPAL a la vez que carga...");
				Toast toast = Toast.makeText(getApplicationContext(), "...Haciendo otra cosa el usuario sobre el hilo PRINCIPAL a la vez que carga...", Toast.LENGTH_SHORT);
				toast.show();
			}
		});

		new DescargarImagenesDeInternetEnOtroHilo(CANCELAR_SI_MAS_DE_100_IMAGENES).execute(" Estos Strings van a variableNoUsada que no usaremos en este ejemplo y podiamos haber declarado como Void "," si lo necesitaramos podemos cambiar el String por otro tipo de datos "," podemos añadir más de 4 datos que los de este ejemplo, todos los que necesitemos "," y recuerda que se usan como un array, para acceder en concreto a este usaríamos variableNoUsada[3] "); //Arrancamos el AsyncTask. el método "execute" envía datos directamente a doInBackground()
	}
```

 

Los que hayan leído otros tutoriales, sabrán que me gusta poner el código lo más compacto y claro posible. La última línea la podíamos haber dividido en dos como la siguiente (Al execute no es necesario pasarle parámetros si no queremos, es más, en este ejemplo no usamos para nada sus valores, aunque podríamos si quisiéremos).

 

```java

DescargarImagenesDeInternetEnOtroHilo miTareaAsincrona = new DescargarImagenesDeInternetEnOtroHilo(CANCELAR_SI_MAS_DE_100_IMAGENES);
miTareaAsincrona.execute();
```

 

Y dentro de nuestra clase “MainActivity” declararemos una clase privada llamada “DescargarImagenesDeInternetEnOtroHilo”. Voy a poner todo el código seguido (En el primer vistazo al código puede que te eche para atrás, pero si omites los comentarios en gris apenas son unas pocas líneas de código 😀 ) y lo explicaré por partes abajo.

 

```java

	private class DescargarImagenesDeInternetEnOtroHilo extends AsyncTask <String, Float, Integer> {
		private boolean cancelarSiHayMas100Archivos;
		private ProgressBar miBarraDeProgreso;

		/**
		 * Contructor de ejemplo que podemos crear en el AsyncTask
		 * 
		 * @param en este ejemplo le pasamos un booleano que indica si hay más de 100 archivos o no. Si le pasas true se cancela por la mitad del progreso, si le pasas false seguirá hasta el final sin cancelar la descarga simulada
		 */
		public DescargarImagenesDeInternetEnOtroHilo(boolean cancelarSiHayMas100Archivos) {
			this.cancelarSiHayMas100Archivos = cancelarSiHayMas100Archivos;
		}

		/**
		 * Se ejecuta antes de empezar el hilo en segundo plano. Después de este se ejecuta el método "doInBackground" en Segundo Plano
		 * 
		 * Se ejecuta en el hilo: PRINCIPAL
		 */
		@Override
		protected void onPreExecute() {
			TV_mensaje.setText("ANTES de EMPEZAR la descarga. Hilo PRINCIPAL");
			Log.v(TAG_LOG, "ANTES de EMPEZAR la descarga. Hilo PRINCIPAL");

			miBarraDeProgreso = (ProgressBar) findViewById(R.id.progressBar_indicador);
		}

		/**
		 * Se ejecuta después de "onPreExecute". Se puede llamar al hilo Principal con el método "publishProgress" que ejecuta el método "onProgressUpdate" en hilo Principal
		 * 
		 * Se ejecuta en el hilo: EN SEGUNDO PLANO
		 * 
		 * @param array con los valores pasados en "execute"
		 * @return devuelve un valor al terminar de ejecutar este segundo plano. Se lo envía y ejecuta "onPostExecute" si ha termiado, o a "onCancelled" si se ha cancelado con "cancel"
		 */
		@Override
		protected Integer doInBackground(String... variableNoUsada) {

			int cantidadImagenesDescargadas = 0;
			float progreso = 0.0f;

			//Suponemos que tenemos 200 imágenes en algún lado de Internet. isCancelled() comprueba si hemos cancelado con cancel() el hilo en segundo plano.
			while (!isCancelled() && cantidadImagenesDescargadas<200){ 
 				cantidadImagenesDescargadas++;
 				Log.v(TAG_LOG, "Imagen descargada número "+cantidadImagenesDescargadas+". Hilo en SEGUNDO PLANO");

 				//Simulamos la descarga de una imagen. Iría aquí el código........................
 				try {
 					//Simula el tiempo aleatorio de descargar una imagen, al dormir unos milisegundos aleatorios al hilo en segundo plano
 					Thread.sleep((long) (Math.random()*10)); 
 				} catch (InterruptedException e) {
 					cancel(true); //Cancelamos si entramos al catch porque algo ha ido mal
 					e.printStackTrace();
 				}
 				//Simulamos la descarga de una imagen. Iría aquí el código........................

 				progreso+=0.5;

 				//Enviamos el progreso a "onProgressUpdate" para que se lo muestre al usuario, pues en el hilo principal no podemos llamar a nada de la interfaz
 				publishProgress(progreso);

 				//Si hemos decidido cancelar al pasar de 100 imágenes descargadas entramos aquí.
 				if (cancelarSiHayMas100Archivos && cantidadImagenesDescargadas>100){ 
					cancel(true);
				}
			}

			return cantidadImagenesDescargadas;
		}

		/**
		 * Se ejecuta después de que en "doInBackground" ejecute el método "publishProgress".
		 * 
		 * Se ejecuta en el hilo: PRINCIPAL
		 * 
		 * @param array con los valores pasados en "publishProgress"
		 */
		@Override
		protected void onProgressUpdate(Float... porcentajeProgreso) {
			TV_mensaje.setText("Progreso descarga: "+porcentajeProgreso[0]+"%. Hilo PRINCIPAL");
			Log.v(TAG_LOG, "Progreso descarga: "+porcentajeProgreso[0]+"%. Hilo PRINCIPAL");

			miBarraDeProgreso.setProgress( Math.round(porcentajeProgreso[0]) );
		}

		/**
		 * Se ejecuta después de terminar "doInBackground".
		 * 
		 * Se ejecuta en el hilo: PRINCIPAL
		 * 
		 * @param array con los valores pasados por el return de "doInBackground".
		 */
		@Override
		protected void onPostExecute(Integer cantidadProcesados) {
			TV_mensaje.setText("DESPUÉS de TERMINAR la descarga. Se han descarcado "+cantidadProcesados+" imágenes. Hilo PRINCIPAL");
			Log.v(TAG_LOG, "DESPUÉS de TERMINAR la descarga. Se han descarcado "+cantidadProcesados+" imágenes. Hilo PRINCIPAL");

			TV_mensaje.setTextColor(Color.GREEN);
		}

		/**
		 * Se ejecuta si se ha llamado al método "cancel" y después de terminar "doInBackground". Por lo que se ejecuta en vez de "onPostExecute"
		 * Nota: Este onCancelled solo funciona a partir de Android 3.0 (Api Level 11 en adelante). En versiones anteriores onCancelled no funciona
		 * 
		 * Se ejecuta en el hilo: PRINCIPAL
		 * 
		 * @param array con los valores pasados por el return de "doInBackground".
		 */
		@Override
		protected void onCancelled (Integer cantidadProcesados) {
			TV_mensaje.setText("DESPUÉS de CANCELAR la descarga. Se han descarcado "+cantidadProcesados+" imágenes. Hilo PRINCIPAL");
			Log.v(TAG_LOG, "DESPUÉS de CANCELAR la descarga. Se han descarcado "+cantidadProcesados+" imágenes. Hilo PRINCIPAL");

			TV_mensaje.setTextColor(Color.RED);
		}

	}
```

 

Creo que está bastante documentado el código y ya deberías de ser capaz de deducir prácticamente todo, pero aun así lo explicaré para no dejar ninguna duda al aire. Al crear el objeto de la clase “DescargarImagenesDeInternetEnOtroHilo” entraremos primero en el constructor y nada más. Al llamar a execute() le decimos al AsyncTask que ponga en marcha el hilo en segundo plano. Haciendo lo siguiente:

 
1. onPreExecute(): asociamos la barra de progreso.
2. doInBackground(): con el “while” y el “Thread.sleep((long) (Math.random()*10))” simulamos la descarga de las imágenes al “descargar” varias y que cada una tarde un rato aleatorio en descargarse. En cada iteración del while llamaremos a “publishProgress()” que enviará el número por el que van descargadas a onProgressUpdate().
3. onProgressUpdate(): Actualizará la barra de progreso cada vez que sea llamado “publishProgress()”. Se hace a la par que doInBackground()
4. onPostExecute() u onCancelled(): cambia el texto, y el color a verde si entramos por onPostExecute() o a rojo si entramos por onCancelled().

 

Fíjate que todas las llamadas a las Views (al TextView) se hacen desde los métodos que empiezan por “on”. Si lo hubiéramos hecho en el doInbackground directamente daría error al incumplirse una de las normas.

 

Por cierto, no dejes de echar un vistazo al Log mientras se ejecuta la aplicación. Si mientras se realiza la acción de descarga (mientras se va llenando la barra), hiciéramos clic tres veces en el botón, obtendríamos un Log similar al siguiente (puede que la siguiente salida del Log la veas diferente; esto es porque la hemos limpiado para quedarnos con lo importante, colocado para que se vea de un vistazo y resumido).

 

```text

ANTES de EMPEZAR la descarga. Hilo PRINCIPAL

Imagen descargada número 1. Hilo en SEGUNDO PLANO
Imagen descargada número 2. Hilo en SEGUNDO PLANO
Imagen descargada número 3. Hilo en SEGUNDO PLANO
Imagen descargada número 4. Hilo en SEGUNDO PLANO
Imagen descargada número 5. Hilo en SEGUNDO PLANO
...
Progreso descarga: 12.0%. Hilo PRINCIPAL
Progreso descarga: 12.5%. Hilo PRINCIPAL
Imagen descargada número 27. Hilo en SEGUNDO PLANO
Imagen descargada número 28. Hilo en SEGUNDO PLANO

...Haciendo otra cosa el usuario sobre el hilo PRINCIPAL a la vez que carga...

Imagen descargada número 29. Hilo en SEGUNDO PLANO
Imagen descargada número 30. Hilo en SEGUNDO PLANO
Progreso descarga: 13.0%. Hilo PRINCIPAL
Progreso descarga: 13.5%. Hilo PRINCIPAL
Progreso descarga: 14.0%. Hilo PRINCIPAL
...
Progreso descarga: 22.5%. Hilo PRINCIPAL
Imagen descargada número 48. Hilo en SEGUNDO PLANO
Progreso descarga: 23.0%. Hilo PRINCIPAL
Progreso descarga: 23.5%. Hilo PRINCIPAL
Imagen descargada número 49. Hilo en SEGUNDO PLANO

...Haciendo otra cosa el usuario sobre el hilo PRINCIPAL a la vez que carga...

Progreso descarga: 24.0%. Hilo PRINCIPAL
Progreso descarga: 24.5%. Hilo PRINCIPAL
Imagen descargada número 50. Hilo en SEGUNDO PLANO
Imagen descargada número 51. Hilo en SEGUNDO PLANO
...
Progreso descarga: 32.0%. Hilo PRINCIPAL
Progreso descarga: 32.5%. Hilo PRINCIPAL
Imagen descargada número 68. Hilo en SEGUNDO PLANO
Imagen descargada número 69. Hilo en SEGUNDO PLANO
Progreso descarga: 33.0%. Hilo PRINCIPAL

...Haciendo otra cosa el usuario sobre el hilo PRINCIPAL a la vez que carga...

Imagen descargada número 70. Hilo en SEGUNDO PLANO
Progreso descarga: 33.5%. Hilo PRINCIPAL
Progreso descarga: 34.0%. Hilo PRINCIPAL
Imagen descargada número 71. Hilo en SEGUNDO PLANO
...
Progreso descarga: 99.0%. Hilo PRINCIPAL
Imagen descargada número 199. Hilo en SEGUNDO PLANO
Progreso descarga: 99.5%. Hilo PRINCIPAL
Imagen descargada número 200. Hilo en SEGUNDO PLANO
Progreso descarga: 100.0%. Hilo PRINCIPAL

DESPUÉS de TERMINAR la descarga. Se han descarcado 200 imágenes. Hilo PRINCIPAL
```

 

En el Log se aprecia muy bien como se entrelazan los hilos, y como uno actúa a la vez que el otro (es mejor verlo en directo, mientras funciona). Y también, estate atento a que si invocamos publishProgress(), no se llama de inmediato a onProgressUpdate(), queriendo decir con esto, que este método está principalmente diseñado para dar feedback al usuario.

 

En el AsyncTask se introducen aspectos avanzados de Java, que puede que no conozcas. Para ello las dos siguientes notas te aclararán del todo los entresijos.

 

**Nota sobre la Inferencia de tipos (<Tipo>)** Si nunca has visto nada de inferencia de tipos, seguramente te sorprenderá ver tipos (Integer, String, etc) entre los símbolos de “mayor que” y “menor que”. Como en la cabecera de la anterior clase:

 

```java

private class DescargarImagenesDeInternetEnOtroHilo extends AsyncTask <String, Float, Integer>
```

 

Habrás descubierto al final que aparece: <String, Float, Integer> Esto es una habilidad que tiene Java y se sirve para hacer a las variables genéricas. ¿Qué quiere decir genéricas? Que la variable puede ser del tipo que tú quieras; es decir, si te interesara que sean todas las variables de tipo Strings podrías poner:

 

```java

private class DescargarImagenesDeInternetEnOtroHilo extends AsyncTask <String, String, String>
```

 

¿Y por qué te puede interesar? Porque los métodos que tiene la clase usarán estas variables con el tipo que tu hayas definido, si necesitas que sea un String ¿Por qué no? Y si necesitas que alguna sea entera, pues también. La única condición es que no se permiten tipos primitivos solo tipos objetos; queremos decir con esto que para un entero no se puede poner “int”, sino “Integer” –para efectos es lo mismo. De la anterior nota, veamos el código entero coloreado con sus tipos inferidos. Fíjate en la cabecera de la clase los tres colores, y como se propagan hacia los tipos de las variables de los métodos que sobrescribe.

 

[![Codigo Asynktask de Android Facil - www.Jarroba.com](https://jarroba.com/wp-content/uploads/2013/09/Codigo-Asynktask-de-Android-Facil-www.Jarroba.com_.png)](https://jarroba.com/wp-content/uploads/2013/09/Codigo-Asynktask-de-Android-Facil-www.Jarroba.com_.png)

 

¿Y por qué te puede interesar? Porque los métodos que tiene la clase usarán estas variables con el tipo que tu hayas definido, si necesitas que sea un String ¿Por qué no? Y si necesitas que alguna sea entera, pues también. La única condición es que no se permiten tipos primitivos solo tipos objetos; queremos decir con esto que para un entero no se puede poner “int”, sino “Integer” –para efectos es lo mismo.

 

De la anterior nota, veamos el código entero coloreado con sus tipos inferidos. Fíjate en la cabecera de la clase los tres colores, y como se propagan hacia los tipos de las variables de los métodos que sobrescribe.

 

 

**Nota sobre los argumentos variables (…)** Puede que sea la primera vez que veas tres puntos en Java como la siguiente línea de código:

 

```java

protected void onProgressUpdate(Float... porcentajeProgreso)
```

 

E incluso te sonará a chino, pero tranquilo. Los tres puntos es casi lo mismo que un array. Me explico, cuando es la variable que se le pasa a una función se trabaja igual que un array normal y corriente:

 

```java

public void hacer (String... variasCosas) {
	Log.v(TAG_LOG, "Contenido: "+ variasCosas [0] + variasCosas [1] + variasCosas [2]);
}
```

 

Fíjate que es lo mismo que (hemos cambiado … por []):

 

```java

public void hacer (String[] variasCosas) {
	Log.v(TAG_LOG, "Contenido: "+ variasCosas [0] + variasCosas [1] + variasCosas [2]);
}
```

 

Solo cambia a la hora de llamar a la función. Si la llamamos como un array normal de toda la vida, ni lo notaremos:

 

```java

int[] arrayDeTodaLaVida = {"Cosa 1", "Cosa 2", "Cosa 3"};
hacer (arrayDeTodaLaVida);
```

 

Pero si te digo que gracias a los tres puntos suspensivos, podemos meterle directamente un solo valor, directamente como:

 

```java

hacer ("Cosa 1");
```

 

O dos:

 

```java

hacer ("Cosa 1", "Cosa 2");
```

 

O tres:

 

```java

hacer ("Cosa 1", "Cosa 2", "Cosa 3");
```

 

O ninguno:

 

```java

hacer ();
```

 

O todos los que queramos directamente, sin tener que declarar el array. Se puede decir que se adaptan los valores que toma la función a lo que necesitamos. Aunque lo que realmente hace es cogerlo todos juntos y meterlos en un array para después trabajar con él como ya vimos. Y como todo array tiene un tamaño (recuerdo que si llamamos a una posición del array que no existe tendremos un bonito error “java.lang Array Index Out Of Bounds Exception”), y se puede recorrer (con for, foreach, while, etc).

 

Con esto te dejo el código y terminamos: [Pulsa aquí para descargar el código completo y funcional del ejemplo de AsyncTask para Android](https://github.com/jarroba/AsyncTask)

 

No podemos despedirnos sin hacer referencia a la página oficial de Android sobre hilos: [Processes and threads overview (Android Developers)](http://developer.android.com/guide/components/processes-and-threads.html)

## Serie: Multitarea en Android

1. [Multitarea en Android](https://jarroba.com/multitarea-en-android.md)
2. [AsyncTask en Android](https://jarroba.com/asynctask-en-android.md)

