AsyncTask en Android


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 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í, 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 Activityes aquí). ¿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. 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

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 hilos 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 el 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

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 AsynkTask 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 🙂 ).

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: 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).

Ejemplo de aplicacion con AsynkTask de Android - www.Jarroba.com

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

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

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

<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 pasa de 100 imágenes. Lo pondremos a “true” si queremos ver cómo funciona al cancelarse el doInBackground().

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.

private final String TAG_LOG = "test";

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

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.

	@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 me 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 sino queremos, es más, en este ejemplo no usamos para nada sus valores, aunque podríamos si quisiéremos).

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.

	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), hicieramos 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).

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:

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:

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

¿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:

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:

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 []):

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:

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:

hacer ("Cosa 1");

O dos:

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

O tres:

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

O ninguno:

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

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

Comparte esta entrada en:
Safe Creative #1401310112503
AsyncTask en Android por "www.jarroba.com" esta bajo una licencia Creative Commons
Reconocimiento-NoComercial-CompartirIgual 3.0 Unported License.
Creado a partir de la obra en www.jarroba.com

75 thoughts on “AsyncTask en Android”

  1. Hola amigo:
    Estoy realizando una aplicación que envía imágenes a un servidor web async-http, lo que quiero es que un hilo secundario gestione este envío. El envío con AsynckHttp tiene un evento onsuccess el cual sucede cuando termina de enviar los datos, precisamente como notificaria a doInBackground para informar que una imagen ha sido enviado y asi mostrar el progreso. He probado con un while dentro de doInBackground consultando con una variable compartida pero no logro funcionar. Algún alcance por favor.

    1. Puedes notificar al hilo principal y mostrar al usuario (como una barra de progreso) desde el hilo secundario llamando a publishProgress() (dentro del doInBackground) y lo recibirías en el hilo principal en el ciclo de vida a onProgressUpdate.

  2. Hola Jarroba,
    Es recomendable usar un único AsyncTask para cada fragment?. O se podría sólo usar single Asynctask para todas las tareas.?. Cuál sería el óptimo?. Muchas gracias.

    1. Se puede utilizar un AsyncTask por cada Fragment, sino quieres repetir código puedes crear una clase aparte e instanciarla directamente o heredar de AsyncTask para crear el tuyo propio. Utilizar un Singleton no es muy recomendado pues puede dar problemas al compartir la ejecución entre Fragments con AsyncTask sino se programa con cuidado.

  3. Buenas, queria consultarte el porque mi codigo no toma el "if" pero si el "else"? si cambio las condicion del if (quito "!") si me funciona y el que deja de funcionar es el "else". Desde ya muchas gracias.

    protected void onPostExecute(String result) {
                JSONArray usuario = null;
                String user;
                try {
                    usuario = new JSONArray(result);

                    user = usuario.getString(0);

                    if (!user.equals(RegistrarDni)){
                        Toast.makeText(getApplicationContext(),"Nuevo Registro",Toast.LENGTH_LONG).show();
                        Intent ingresar = new Intent(RegistroSistema.this, UsuarioNuevo.class);
                        ingresar.putExtra("DNIregistro", RegistrarDni);
                        startActivity(ingresar);
                    }else {
                        Toast.makeText(getApplicationContext(),"Usuario Existente",Toast.LENGTH_LONG).show();
                    }
                }catch (JSONException e){
                    e.printStackTrace();
                }
            }
        }

    1. Aquí lo mejor que puedes hacer es comprobar que valores te devuelve antes del if las variables user y RegistrarDni, pues es donde está el problema. También el equals() es un poco peculiar para ciertos objetos, así que dependerá de los objetos que sean tus variables (si son Strings debería de funcionar bien).

  4. Hola Ramon Invarato , hola A todos los que estan leyendo este mensaje , pues bien es que tengo una duda y no se como preguntar en internet acerca de esto, es que no entiendo esta construccion del codigo en la parte de la clase que extiende de AsyncTask ->

    private class DescargarImagenesDeInternetEnOtroHilo extends AsyncTask <String, Float, Integer>{}

    mi pregunta es :

    Para que sirven esos parametros que Tiene AsyncTask , Que representa esto?

     

    1. Lo explico al final del tutorial en el penultimo cuadro azul.

      Una explicación rápida. Imagina que quieres crear la siguiente clase:

      class miClase{

      private Integer valor:

      }

      En la anterior clase el valor es siempre entero, ¿Qué pasa si queremos que sea cualquiera? Pues

      class miClase <Integer>{

      private <tipo> valor:

      }

      o

      class miClase <String>{

      private <tipo> valor:

      }

      Con los tipos genéricos puede ser cualquier cosa una misma variable.

  5. Hola Ramon, tengo una duda, necesito algo muy basico pero que me tiene loco, estoy usando una base de datos sqlite encriptada con sqlitecipher , el asunto es que se demora un mundo en cargar porque el sqlcipher es bien lento , mi aplicaci'on se va a usar en cels actuales (donde el tiempo de carga es infimo) pero en antiguos tambien (froyo) donde la pausa es exttemadamente laga puede llegar a los 10 segundos con solo abrir una base de datos con solo un registro, como puedo hacer en asynctask para que mientras la app este leyendo la base de datos muestre una ventana de cargando por favor (para que el usuario se de cuenta que no hay problemas ) y que cuando termine de cargar la base se quite de la pantalla.

    saludos

    1. Hola. Te recomiendo que en segundo plano (doInBackground) hagas el descifrado de datos, mientras notificas al usuario con una ventana emergente. Con este tutorial tienes todo lo que necesitas, solo te queda poner el cifrado en segundo plano 🙂

  6. Hola, tengo problemas en mi aplicacion android con el sockettimeout y las asynctask… ¿como puedo sacar un mensaje Toast (por ejemplo) en mi activity, si no consigue conectar al servidor, por que esta apagado o no es correcta la direccion IP?

    Tengo esto en una clase ‘Configuracion’:

    public static void conectar()
    {
    try
    {
    socketCliente = new Socket();
    socketCliente.connect(new InetSocketAddress(DIRECCION, PUERTO), 5000);

    }

    catch (SocketTimeoutException e)
    {
    e.printStackTrace();
    }

    Desde el doInBackground (en otra clase ‘CargarLista’) llamo al metodo conectar anterior:

    protected LinkedList doInBackground(Void… params)
    {
    try
    {

    Configuracion.conectar();

    Dado que conectar se ejecuta en un hilo y el doInBackground se ejecuta en otro hilo diferente al principal, no puedo capturar el SocketTiemoutException. ¿Que hago para avisar que han pasado los 5 segundos de espera? ¿Donde saco el mensaje Toast? ¿Voy desencaminado?

    Gracias!

    1. Hola Jorge,
      puedes saber que mensaje de error devuelve el mensaje del servidor al establecer la conexión con HttpURLConnection con la conexión, te devuelve el número del error el método getResponseCode().

    1. Hola Pablo,

      en ejecución no se puede pasar variables por los métodos normales desde fuera de la clase que hereda de AsyncTask. Podrías crear una variable global y modificarla mientras se ejecuta, pero te puede dar problemas.

  7. Hola estoy haciendo un curso de android y en la practica del tema 4 consiste en realizar una aplicación con la que cargaremos en los maps de Google imágenes que estarán geoposicionadas. Dichas imágenes estarán almacenadas en una base de datos de MySQL y dispondremos de un servidor php real. Veremos cómo configurar dicho servidor. Cuando arranquemos la aplicación, nos cargará el mapa en la posición en la que nos encontramos, y nos mostrará las imágenes que nosotros hayamos configurado que se las descargarán directamente del servidor, a través de la red, que hemos configurado previamente en la web.

    La aplicación cuando la ejecuto en el emulador o en mi telefono y doy a cualquier opcion para que me muestre la posicion donde está el establecimiento se para la aplicación.

    package com.learnimbus.locations.activityprincipal;

    import java.util.ArrayList;

    import com.google.android.gms.maps.CameraUpdateFactory;
    import com.google.android.gms.maps.GoogleMap;
    import com.google.android.gms.maps.SupportMapFragment;
    import com.google.android.gms.maps.model.BitmapDescriptorFactory;
    import com.google.android.gms.maps.model.LatLng;
    import com.google.android.gms.maps.model.MarkerOptions;
    import com.learnimbus.locations.herramientasmapas.Localizaciones;
    import com.learnimbus.locations.herramientasmapas.WebserviceUtil;

    import android.content.Context;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.sax.StartElementListener;
    import android.support.v4.app.FragmentActivity;
    import android.view.Menu;
    import android.widget.Toast;

    public class LearnimbusLocationsValladolid extends FragmentActivity {

    private GoogleMap mapa = null;
    private String tipoRecibido = null;
    private ArrayList listadomarcadores= null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.mapa);
    mapa = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
    // Recuperamos los lugares depende de la que se haya pulsado

    Bundle extras = getIntent().getExtras();
    if (extras != null) {
    tipoRecibido = extras.getString(VistaListaIconos.MyKey);

    LlamarWebService llamarWebservice = new LlamarWebService();
    llamarWebservice.applicationContext = this;
    llamarWebservice.execute();
    }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.learnimbus_locations_valladolid, menu);
    return true;
    }

    public void TaskCallback(){
    for(int i=0; i < listadomarcadores.size(); i++){
    mapa.addMarker(listadomarcadores.get(i));
    }
    mapa.moveCamera(CameraUpdateFactory.zoomTo((float) 16.0));
    LatLng valladolid = new LatLng(41.655832, -4.738369);
    mapa.moveCamera(CameraUpdateFactory.newLatLng(valladolid));
    }

    public class LlamarWebService extends AsyncTask {

    protected Context applicationContext;

    @Override
    protected String doInBackground(Void… params) {
    WebserviceUtil webservice = new WebserviceUtil();
    ArrayList listalocations = webservice.cargarLocalizaciones(tipoRecibido);
    listadomarcadores = new ArrayList();
    for(int i=0; i < listalocations.size(); i++){
    Localizaciones localizacionObj = listalocations.get(i);
    String cordenadas[] = { localizacionObj.getLongitud(), localizacionObj.getLatitud() };
    double lat = Double.parseDouble(cordenadas[0]);
    double lng = Double.parseDouble(cordenadas[1]);
    int icono = R.drawable.learnimbusmenu;
    if(localizacionObj.getTipo().equals("BANCO")){
    icono = R.drawable.visa;
    }else if(localizacionObj.getTipo().equals("GASOLINERA")){
    icono = R.drawable.gas;
    }else if(localizacionObj.getTipo().equals("HOSPITAL")){
    icono = R.drawable.hospital;
    }else if(localizacionObj.getTipo().equals("RESTAURANTE")){
    icono = R.drawable.restaurant;
    }else if(localizacionObj.getTipo().equals("TIENDA")){
    icono = R.drawable.shop;
    }else if(localizacionObj.getTipo().equals("LEARNIMBUS")){
    icono = R.drawable.learnimbusmenu;
    }

    listadomarcadores.add(new MarkerOptions().position(new LatLng(lat, lng)).title(localizacionObj.getNombre()).snippet(localizacionObj.getDescripcion()).icon(BitmapDescriptorFactory.fromResource(icono)));
    }
    return "OK";
    }

    @Override
    protected void onPostExecute(String result) {
    super.onPostExecute(result);
    ((LearnimbusLocationsValladolid)this.applicationContext).TaskCallback();
    }
    }

    }
    _______________________________________________________________________________________
    package com.learnimbus.locations.activityprincipal;

    import java.util.ArrayList;

    import com.learnimbus.locations.herramientasmapas.Local;
    import android.app.ListActivity;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.ArrayAdapter;
    import android.widget.ImageView;
    import android.widget.ListView;
    import android.widget.TextView;

    public class VistaListaIconos extends ListActivity{
    private ArrayList m_locals = null;
    private IconListViewAdapter m_adapter;
    final public static String MyKey = «mikey»;

    @Override
    public void onCreate(Bundle savedlnstanceState){
    super.onCreate(savedlnstanceState);
    setContentView(R.layout.lista);

    // Al crear la clase se inicializa el Listview que muestra los menus
    m_locals = new ArrayList();

    this.m_adapter = new IconListViewAdapter(this,R.layout.iconrow,m_locals); setListAdapter(this.m_adapter);
    inicializarSitios();
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
    Bundle bundle = new Bundle();
    Local local = (Local) l.getItemAtPosition(position);
    // Cargamos el Bundle con la coleccion de objetos que pasaremos
    // a la siguiente vista Bundle bundle = new Bundle();
    bundle.putString(MyKey, local.getLocalName());
    // Creamos la vista de Lista de objetos y le pasamos la
    // colección de objetos a mostrar
    Intent mylntent = new Intent(this, LearnimbusLocationsValladolid.class); mylntent.putExtras(bundle);
    startActivityForResult(mylntent, 0);
    }

    private void inicializarSitios() {
    try {
    // creamos los sitios
    m_locals = new ArrayList();
    Local o1 = new Local();
    o1.setLocalName(«Bancos»);
    o1.setLocalImage(R.drawable.visa);
    Local o2 = new Local();
    o2.setLocalName(«Gasolineras»);
    o2.setLocalImage(R.drawable.gas);
    Local o3 = new Local();
    o3.setLocalName(«Hospitales»);
    o3.setLocalImage(R.drawable.hospital);
    Local o4 = new Local ();
    o4.setLocalName(«Restaurantes»);
    o4.setLocalImage(R.drawable.restaurant);
    Local o5 = new Local();
    o5.setLocalName(«Tiendas»);
    o5.setLocalImage(R.drawable.shop);
    Local o6 = new Local();
    o6.setLocalName(«Learn Nimbus»);
    o6.setLocalImage(R.drawable.learnimbusmenu);
    Local o7 = new Local();
    o7.setLocalName(«Todos»);
    o7.setLocalImage(R.drawable.all);

    // añadimos los sitios al Array
    m_locals.add(o1);
    m_locals.add(o2);
    m_locals.add(o3);
    m_locals.add(o4);
    m_locals.add(o5);
    m_locals.add(o6);
    m_locals.add(o7);

    Log.i(«Locales añadidos «, «» + m_locals.size()) ;
    } catch (Exception e) {
    Log.e(«BACKGROUND_PROC», e.getMessage());
    }
    if (m_locals != null && m_locals.size() >0) {
    for (int i = 0; i < m_locals.size(); i++){
    m_adapter.add(m_locals.get(i));
    }
    }
    m_adapter.notifyDataSetChanged();
    }

    public class IconListViewAdapter extends ArrayAdapter {
    private ArrayList items;

    private IconListViewAdapter(Context context, int textViewResourceId, ArrayList items){
    super(context, textViewResourceId, items);
    this.items = items;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
    View v = convertView;
    if(v == null){
    LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    v = vi.inflate(R.layout.iconrow, null);
    }
    Local o = items.get(position);
    if(o != null){
    // poblamos la lista de elementos
    TextView tt = (TextView) v.findViewById(R.id.row_toptext);
    ImageView im = (ImageView) v.findViewById(R.id.icon);
    if(im != null){
    im.setImageResource(o.getLocalImage());
    }
    if(tt != null){
    tt.setText(o.getLocalName());
    }
    }
    return v;
    }
    }

    }
    _________________________________________________________________________________________
    package com.learnimbus.locations.herramientasmapas;

    public class Local {
    private String localName;
    private int localImage;

    public String getLocalName() {
    return localName;
    }
    public void setLocalName(String localName) {
    this.localName = localName;
    }
    public int getLocalImage() {
    return localImage;
    }
    public void setLocalImage(int localImage) {
    this.localImage = localImage;
    }

    }
    _____________________________________________________________________________________
    package com.learnimbus.locations.herramientasmapas
    public class Localizaciones {
    private int ID;
    private String NOMBRE;
    private String DESCRIPCION;
    private String LONGITUD;
    private String LATITUD;
    private String TIPO;

    public int getId() {
    return ID;
    }
    public void setId(int ID) {
    this.ID = ID;
    }
    public String getNombre() {
    return NOMBRE;
    }
    public void setNombre(String NOMBRE) {
    this.NOMBRE = NOMBRE;
    }
    public String getDescripcion() {
    return DESCRIPCION;
    }
    public void setDescripcion(String DESCRIPCION) {
    this.DESCRIPCION = DESCRIPCION;
    }
    public String getLongitud() {
    return LONGITUD;
    }
    public void setLongitud(String LONGITUD) {
    this.LONGITUD = LONGITUD;
    }
    public String getLatitud() {
    return LATITUD;
    }
    public void setLatitud(String LATITUD) {
    this.LATITUD = LATITUD;
    }
    public String getTipo() {
    return TIPO;
    }
    public void setTipo(String TIPO) {
    this.TIPO = TIPO;
    }

    }
    ________________________________________________________________________________________
    package com.learnimbus.locations.herramientasmapas;

    import java.io.IOException;
    import java.util.ArrayList;

    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.ResponseHandler;
    import org.apache.http.client.methods.HttpPost;
    import org.apache.http.impl.client.BasicResponseHandler;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.params.BasicHttpParams;
    import org.apache.http.params.HttpConnectionParams;
    import org.apache.http.params.HttpParams;
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;

    public class WebserviceUtil {
    int TIMEOUT_MILLISEC = 10000; // = 10 seconds

    /**
    * Recupera las ciudades desde el webservice
    * @return ArrayList
    */
    public ArrayList cargarLocalizaciones(String tipo){
    String url = «http://pruebaslearnimbus.herobo.com/consultas.php?formato=JSON&action=»+tipo;
    ArrayList listaLocalizaciones = null;

    // Parse
    JSONArray jArray = cargaDesdeWebservice(url);

    listaLocalizaciones = new ArrayList();

    for (int i = 0; i < jArray.length(); i++) {
    Localizaciones localizacionObj = null;

    JSONObject e;
    try {
    e = jArray.getJSONObject(i);
    String s = e.getString("post");
    JSONObject jObject = new JSONObject(s);

    //Creamos un nuevo objeto localización y lo cargamos con el resultado de la BBDD
    localizacionObj = new Localizaciones();
    localizacionObj.setId(jObject.getInt("ID"));
    localizacionObj.setNombre(jObject.getString("NOMBRE"));
    localizacionObj.setDescripcion(jObject.getString("DESCRIPCION"));
    localizacionObj.setLongitud(jObject.getString("LONGITUD"));
    localizacionObj.setLatitud(jObject.getString("LATITUD"));
    localizacionObj.setTipo(jObject.getString("TIPO"));

    // Añadimos al listado el objeto
    listaLocalizaciones.add(localizacionObj);
    } catch (JSONException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
    }
    }

    return listaLocalizaciones;
    }

    /**
    * Conecta y recupera la cadena JSON del webservice
    * @param url
    * @return
    */
    private JSONArray cargaDesdeWebservice(String url){
    JSONArray jArray = null;

    HttpParams httpParams = new BasicHttpParams();
    HttpConnectionParams.setConnectionTimeout(httpParams, TIMEOUT_MILLISEC);
    HttpConnectionParams.setSoTimeout(httpParams, TIMEOUT_MILLISEC);

    HttpClient httpclient = new DefaultHttpClient(httpParams);
    HttpPost httppost = new HttpPost(url);

    ResponseHandler responseHandler = new BasicResponseHandler();
    String responseBody;
    try {
    responseBody = httpclient.execute(httppost, responseHandler);

    // Parse
    JSONObject json = new JSONObject(responseBody);
    jArray = json.getJSONArray(«posts»);
    } catch (ClientProtocolException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (JSONException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

    return jArray;
    }

    }
    ______________________________________________________________________________________
    Androidmanifest.xml

    ______________________________________________________________________________________
    iconrow.xml

    _______________________________________________________________________________________
    lista.xml

    ______________________________________________________________________________________
    mapa.xml

    _______________________________________________________________________________________
    consultas.php
    $post);
    }
    }

    if($formato == ‘json’) {
    header(‘Content-type: application/json’);
    echo json_econde(array(‘posts’=>$posts));
    }
    else {
    header(‘Content-type: text/xml’);
    echo ‘ ‘;
    foreach($posts as $index => $post) {
    if(is_array($post)) {
    foreach($post as $key => $value) {
    echo »;
    if(is_array($value)) {
    foreach($value as $tag => $val) {
    echo », htmlentities($val),»;
    }
    }
    echo »;
    }
    }
    }
    echo ‘ ‘;
    }

    @mysql_close($link);
    }
    ?>
    _____________________________________________________________________________________
    Hay algún error en alguno de los códigos?

      1. En el logcat al compilarlo me sale todo esto
        11-19 21:03:59.964: E/AndroidRuntime(1726): FATAL EXCEPTION: main
        11-19 21:03:59.964: E/AndroidRuntime(1726): java.lang.NoClassDefFoundError: com.learnimbus.locations.activityprincipal.LearnimbusLocationsValladolid
        11-19 21:03:59.964: E/AndroidRuntime(1726): at com.learnimbus.locations.activityprincipal.VistaListaIconos.onListItemClick(VistaListaIconos.java:45)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at android.app.ListActivity$2.onItemClick(ListActivity.java:319)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at android.widget.AdapterView.performItemClick(AdapterView.java:298)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at android.widget.AbsListView.performItemClick(AbsListView.java:1100)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at android.widget.AbsListView$PerformClick.run(AbsListView.java:2749)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at android.widget.AbsListView$1.run(AbsListView.java:3423)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at android.os.Handler.handleCallback(Handler.java:725)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at android.os.Handler.dispatchMessage(Handler.java:92)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at android.os.Looper.loop(Looper.java:137)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at android.app.ActivityThread.main(ActivityThread.java:5041)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at java.lang.reflect.Method.invokeNative(Native Method)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at java.lang.reflect.Method.invoke(Method.java:511)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
        11-19 21:03:59.964: E/AndroidRuntime(1726): at dalvik.system.NativeStart.main(Native Method)

        1. El error que me da de nuevo
          11-20 15:05:03.500: E/AndroidRuntime(1101): FATAL EXCEPTION: AsyncTask #1
          11-20 15:05:03.500: E/AndroidRuntime(1101): java.lang.RuntimeException: An error occured while executing doInBackground()
          11-20 15:05:03.500: E/AndroidRuntime(1101): at android.os.AsyncTask$3.done(AsyncTask.java:299)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at java.util.concurrent.FutureTask.run(FutureTask.java:239)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at java.lang.Thread.run(Thread.java:856)
          11-20 15:05:03.500: E/AndroidRuntime(1101): Caused by: java.lang.NullPointerException
          11-20 15:05:03.500: E/AndroidRuntime(1101): at com.learnimbus.locations.herramientasmapas.WebserviceUtil.cargarLocalizaciones(WebserviceUtil.java:36)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at com.learnimbus.locations.activityprincipal.LearnimbusLocationsValladolid$LlamarWebService.doInBackground(LearnimbusLocationsValladolid.java:71)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at com.learnimbus.locations.activityprincipal.LearnimbusLocationsValladolid$LlamarWebService.doInBackground(LearnimbusLocationsValladolid.java:1)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at android.os.AsyncTask$2.call(AsyncTask.java:287)
          11-20 15:05:03.500: E/AndroidRuntime(1101): at java.util.concurrent.FutureTask.run(FutureTask.java:234)
          11-20 15:05:03.500: E/AndroidRuntime(1101): … 4 more

          1. Por mas que lo he leido los manuales y demás no veo que tengo que modificar en el doinbackground para que me funcione el proyecto. Si me lo puedes revisar a ver que tengo que modificar.

          2. Huele un poco la última línea antes del return, donde haces listadomarcadores.add(), por estar asignando un icon al mapa puede que esté dando problemas ahí. Prueba a comentarla y a ver que pasa.

          3. Probe a comentar esa linea que me especificas en el codigo y me sigue saliendo
            11-27 19:01:15.143: E/AndroidRuntime(1158): FATAL EXCEPTION: AsyncTask #1
            11-27 19:01:15.143: E/AndroidRuntime(1158): java.lang.RuntimeException: An error occured while executing doInBackground()
            11-27 19:01:15.143: E/AndroidRuntime(1158): at android.os.AsyncTask$3.done(AsyncTask.java:299)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at java.util.concurrent.FutureTask.run(FutureTask.java:239)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at java.lang.Thread.run(Thread.java:856)
            11-27 19:01:15.143: E/AndroidRuntime(1158): Caused by: java.lang.NullPointerException
            11-27 19:01:15.143: E/AndroidRuntime(1158): at com.learnimbus.locations.herramientasmapas.WebserviceUtil.cargarLocalizaciones(WebserviceUtil.java:35)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at com.learnimbus.locations.activityprincipal.LearnimbusLocationsValladolid$LlamarWebService.doInBackground(LearnimbusLocationsValladolid.java:72)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at com.learnimbus.locations.activityprincipal.LearnimbusLocationsValladolid$LlamarWebService.doInBackground(LearnimbusLocationsValladolid.java:1)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at android.os.AsyncTask$2.call(AsyncTask.java:287)
            11-27 19:01:15.143: E/AndroidRuntime(1158): at java.util.concurrent.FutureTask.run(FutureTask.java:234)
            11-27 19:01:15.143: E/AndroidRuntime(1158): … 3 more

          4. Prueba a comentar esa línea y las referencias que tenga (posiblemente por lo que te salga el null). En otras palabras, vamos a depurar el código quitando todo lo que hace después para ver donde puede estar el fallo.

  8. hola, buen trabajo..
    mi problema es el siguiente:
    uso AsyncTask para conectar mi base de datos por medio de un socket y poder intercambiar los datos y solicitudes, que funciona bien, el problema es que quiero abrir un segundo activity y que ejecute de inmediato el Asynctask pero no me esta funcionando y no se cual es el error.
    –fragmento del codigo:
    /**eso es parte del activiy se supone que deberia ejecutar el hiilo para abrir el socket pero no lo hace*/
    new connectTask().execute(«»);
    if (mTcpClient != null) {
    Toast.makeText(MenuPrincipal.this, «enviando: » + id + «.», Toast.LENGTH_SHORT).show();
    mTcpClient.sendMessage(cadena.toString());
    }

    /**y esto doInBackground*/
    protected TCPClient doInBackground(String… message) {
    Toast.makeText(MenuPrincipal.this, «en el hilo», Toast.LENGTH_SHORT).show();
    //we create a TCPClient object and
    mTcpClient = new TCPClient(new TCPClient.OnMessageReceived() {
    @Override
    //here the messageReceived method is implemented
    public void messageReceived(String message) {
    //this method calls the onProgressUpdate
    publishProgress(message);
    }
    });
    mTcpClient.run();

    return null;
    }

    1. El probrema es en hacer por un lado algo en una Activity y lanzar un AsynckTask en otra Activity diferente. El AsynckTask se asocia con la Activity, es decir, vive mientras viva y se ejecuta en segundo plano mientras esté la Activity al frente del usuario. Las soluciones depende de la necesidad son: puedes o bien meter el AsyncTask en un Fragment para hacer todo junto en la misma Activity; o bien crear un Service.

      1. Eso lo tengo claro: un asyncTask para cada activity… el problema es que no sale mensaje de error ni nada, es como si no ejecutara:
        logeo (con su propio asynctask)
        habilita el menu principal que es una lista que se solicita a la bd por medio de su propio asyntask
        y la lista no aparece.

        Probe poniendo mensajes en casi to el codigo del Activity, pero cuando ejecuto el asyntask, el codigo de doInBackground: no realiza acción alguna, solo lo hace el preExecute(pero es obvio por que corresponde al hilo principal).

        Despues lo probe unitariamente y si funciona, sin ningun problema, entonces no se lo que ocurre.
        Solo entiendo que falla la integracion

        1. Haz una cosa, lanza los dos hilos a la vez desde el mismo Activity y me dices que te sale.

          Dando una segunda vuelta a tu código he visto que lanzas un Toast dentro del doInBackground(), no deberías lanzarlo ahí, pues dibuja en pantalla y podría darte problemas.

          1. ok, mira el codigo y me dices que encuentras, pero para recordar, unitariamente funcionan, al realizar la integracion es que falla
            [Dato borrado por seguridad. Más información en http://jarroba.com/faq/ ]

          2. He revisado el código. Lo que te está ocurriendo es tienes una Activity con un AsyncTask, que obtiene el JSON para abrir una nueva Activity con otro AsyncTask. Por lo que el primer AsyncTask deja de funcionar pues su Activity quedó oculta. Por eso te funcionan sueltos pero no juntos. Te recomiendo que uses Fragments o que lances el otro AsyncTask sin abrir otra Activity, esto depende del diseño.

            Borro los links al código por tu privacidad; te aconsejo que cambies las claves que tienes en el código y que pongas como privado los links 😉

          3. OK, ya desvincule los link…
            Osea lo que tendria que hace es no llamar el segundo activity desde dentro del asyntask, para manetener el formato que tengo

          4. Sí, podrías hacer eso. En tu caso lo mejor es que crees los AsyncTask en clases individuales, así no tendrías problemas con la segunda Activity. Si necesitas que el usuario vea algo, puede que tengas que plantear usar Fragments.

          5. OLA, resolví el problema, ingenuamente dejaba la conexión del socket abierto, entonces el asynctask quedaba corriendo, por lo tanto el llamado que realizaba al principio siempre se generaba dentro del hilo principal y la conexión del socket. Por lo que era como tratar llamar al socket dentro del socket….

            Gracias por tu ayuda, ahora puedo continuar tranquilo

  9. Esta duda es más de programación…
    Estoy programando con Eclipse en donde uso Activity… pero la clase donde trabajo ya usa el extends Activity:

    public class Configuracion extends Activity{


    protected void onCreate(…){
    }
    public class empiezaPrograma(View v){
    ….
    }
    }
    Donde aplico el «extends AsyncTask»???

  10. Hola estimado jarroba, estoy desarrollando una aplicación donde tengo que graficar los datos de un sensor provenientes de un servidor web vía wifi, así que para esto use la clase asyncTask que me permite recibir los datos en un hilo paralelo, y la recepción esta hecha usando sockets y configurado el android como cliente. Los datos los voy graficando en tiempo real en un androidPlot, es decir la curva se va formando a medida que llegan lo datos, hasta ahí no hay ningún problema. Pero usando el mismo plot debo graficar 2 curvas de 2 sensores diferentes, primero una y luego la otra, cada sensor me enviará 100 valores cada vez que desde la interfaz se le diga que empiece a enviar los datos, y como los sensores están midiendo cosas diferentes el rangeLabe y el domainLabel tienen cambiar de acuerdo a la curva que se este dibujando. Para esto he puesto dos botones en el hilo principal, uno por cada sensor, por ahora lo único que quiero es que cada botón simplemente cambie los nombres del range y el domain para que correspondan con las medidas de su sensor. Y aquí es donde tengo el problema puesto que los botones no responden, no cambian el nombre de los ejes. Solo lo hacen cuando ya estoy recibiendo datos pero no antes que es cuando necesito. No se cuál es el problema, pero si me puedes ayudar te estaría infinitamente agradecido…

  11. Hola buenas usando asynctask con activity todo me funciona perfecto. La pregunta es como seria usando fragment??
    Ya que probado con fragment y nunca ejecto el doinbackground.

    De ante mano gracias por su ayuda

  12. Hola una pregunta, que programa utilizas , para hacer esos diseños ( smartphones por ejemplo), o que programas me recomiendas para hacer diseños para una presentación de un proyecto. Un saludo genios, los admiro

    1. Hola,
      para presentaciones lo más aconsejable es crear alguna con un editor de prestaciones (Powerpoint, presentaciones de Drive, Prezzi, entre otros). No utilizamos un solo programa para los gráficos, utilizamos varios dependiendo de la situación, pero así los que más utilizo personalmente son Visio, Gimp e Inkscape.

      1. Muchas gracias por responder Ramón , yo les sigo a ustedes desde hace mucho, por eso recurro a ustedes, pero yo me refería que programa uso para dibujar «cualquier cosa»,o una instalación de climatización, o un simple esquema, por ejemplo hoy vi unos dibujos de instalaciones de automatismo industrial con sus bombas de calor, ventiladores, calderas, y mas cosas , y no creo que lo hayan hecho con paint. Un saludo y muchas gracias de antemano cracks.

  13. Estimados, nada mas que agradecer infinitamente el nivel de detalle que utilizan para enseñar. Ojala hubieran mas como ustedes.
    Saludos.

  14. Hola señores Jarroba, estuvo muy bueno el tuto. Tengo una duda. Al momento de cambiar de actividad a una nueva, los procesos que estan en segundo plano se siguen ejecutando, o se destruyen con la actividad que los ejecuto, para dar paso a la nueva actividad.

  15. Hola! Tengo un hilo ejecutado en segundo plano por medio de AsyncTask,
    hasta ahi todo me va bien. Se me actualiza la GUI y no tengo problemas,
    hice un boton Cancelar q llame a cancel(true) para detener el hilo, pero no me funciona y no se por que,
    en el while del hilo tengo un if (isCancelled())break;} … he intentado de todo y no se por que mi hilo
    no se detiene.

    Es un cronometro y al darle a Cancelar sigue normalmente. S: Me podrias echar la mano por favor.

    1. Hola Jose,

      Por lo que comentas tienes bien implementadas las acciones de cancelar. Pero el problema puede venir por varios motivos. Primero que lo estés ejecutando en una versión antigua de Android y no soporte el método onCancelled() (soportado desde el API 11), por lo que el hilo seguirá funcionando (para solventar esto no queda más que tratar la acción de cancelar a mano). Otro problema puede ser que estés llamando al método onCancelled() que es el antiguo y no funciona correctamente; tienes que llamar al que se le pasan parámetros, que sí funciona, que es onCancelled(Result result).

      1. El problema es que si lo intento detener de forma manual tampoco me funciona, con una variable de control o algo asi lo he intentado y no se por que nunca se detiene.

        Anteriormente habia probado hacerlo con Thread y con el handler escribir en la interfaz y tambien escribia todo
        bien pero no podia detenerlo por los mismos motivos, no se que es lo q estoy haciendo mal.

          1. Seria de mucha ayuda, de paso muchas gracias por responder 🙂

            Aqui va el codigo, copiare solo lo importante para q no quede largo.
            Lo que hace cuando uno le da al botonIniciar es darle una animacion al boton cambiandole
            la imagen y tal… hasta ahi todo bien. Pero si quisiera pararlo no puedo.

            Mira, en el main llamo a el metodo onClick, esto es en el MAIN.

            public void onClick(View v) {

            BotonLetraAnimar b = new BotonLetraAnimar(boton); //es la instancia de la clase q lanza el hilo

            if (v.getId() == findViewById(R.id.botonIniciar).getId()) {
            b.execute();
            }
            if (v.getId() == findViewById(R.id.botonCancelar).getId()) {
            b.cancel(true);
            }}

            La clase que tiene el hilo q se llama BotonLetraAnimar seria esta:

            public class BotonLetraAnimar extends AsyncTask {

            Button boton;

            public BotonLetraAnimar(Button boton) {
            super();
            this.boton = boton;
            }

            @Override
            protected Result doInBackground(Void… params) {

            int i;
            for(i = 1; i<20; i++){
            dormir(i); //es un metodo de la clase q es el q lleva el thread.sleep()
            publishProgress(boton);
            if(isCancelled()){break;} //aqui valida si fue cancelado
            }
            return null;
            }

            @Override
            protected void onProgressUpdate(Button… values) {
            super.onProgressUpdate(values);
            boton.setBackgroundResource(obtenerImagen()); //obtener imagen es un metodo interno
            }

            @Override
            protected void onCancelled(Result result) {
            super.onCancelled(result);
            }
            }

          2. He probado el código que me has mandado y funciona bien, cancela al pulsar el botón. Puede que lo que te pase es que cancelas cuando acaba, al tener tan pocas iteraciones el bucle termine antes de que puedas siquiera pulsar el botón.

            Entiendo que estás llamando correctamente al diseño apropiado en setContentView() y que buscas al botón correctamente con:
            Button boton = (Button) findViewById(R.id.button1);

            También he visto que asignas el evento de clicado desde el XML (ya hace tiempo que no lo recomiendo en favor de la asignación en Java con setOnCLickListener(), aunque en este caso no da problemas). En caso de que lo quieras así, asegúrate de que estás llamando a la función onClick() que has declarado en Java, como en este ejemplo:
            <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/textView1"
            android:layout_marginLeft="64dp"
            android:layout_marginTop="29dp"
            android:layout_toRightOf="@+id/textView1"
            android:onClick="onClick"
            android:text="Button" />

            Prueba a loguear el código, te paso como lo he probado y te funciona bien:
            @Override
            protected Result doInBackground(Void... params) {

            int i;
            for (i = 1; i < 200; i++) { Log.v("test", "iteración" + i); 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(); } publishProgress(boton); if (isCancelled()) { Log.v("test", "CANCELADO"); break; } } return null; }

            Ya me cuentas 🙂

          3. public class BotonLetraAnimar extends AsyncTask ** (lleva esos parametros q no puse perdon)

  16. Hola, una consulta. comprendo lo que está aquì y està muiy bueno.
    en un proyecto se mne ha hecho necesario usar un asynctask dentro de otro.
    y no logro hacer cambios en la GUI desde el segundo hilo que está dentro del primero.
    en fin lo que tengo es eso:
    hilo principal
    -> asyncTaks1 (desde acá puedo hacer un setText())
    -> asyncTask2 (desde acá puedo NO hacer un setText())

    alguna sugerencia estimado amigo, de ante mano se agradece.

    atte.

  17. Que tal amigo como harías esto pero el asynctask en una clase diferente a la mainactivity como le asignarías el valor al textview desde la clase asynctask?

  18. Muy buen post 😀 gracias, una consulta como puedo publicar el progreso si el progreso se realiza en una clase distinta, me explico:

    doInBackground(void){
    Usuario.obtenerUsuarios();
    }
    como puedo publicar el progreso si la tarea la realizo en otra clase, gracias de antemano

    1. Buenas Gonzalo,

      si es una clase normal, dentro del propio cuerpo del AsyncTask, en su constructor, puedes instanciar la clase y usarla de manera normal. Eso sí, ten en cuenta que la clase no utilice nada de la interfaz gráfica como se indica en el artículo.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

Uso de cookies

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies

ACEPTAR
Aviso de cookies