Todos los componentes de una aplicación Android, tanto las actividades, los servicios [sí, también los servicios], o los broadcast receivers se ejecutan en el mismo hilo de ejecución, el llamado hilo principal, main thread o GUI thread, que como éste último nombre indica también es el hilo donde se ejecutan todas las operaciones que gestionan la interfaz de usuario de la aplicación. Es por ello, que cualquier operación larga o costosa que realicemos en este hilo va a bloquear la ejecución del resto de componentes de la aplicación y por supuesto también la interfaz, produciendo al usuario un efecto evidente de lentitud, bloqueo, o mal funcionamiento en general, algo que deberíamos evitar a toda costa. Incluso puede ser peor, dado que Android monitoriza las operaciones realizadas en el hilo principal y detecta aquellas que superen los 5 segundos, en cuyo caso se muestra el famoso mensaje de “Application Not Responding” (ANR) y el usuario debe decidir entre forzar el cierre de la aplicación o esperar a que termine.
Obviamente, éstos son el tipo de errores que nadie quiere ver al utilizar su aplicación, y en este artículo y los siguientes vamos a ver varias formas de evitarlo utilizando procesos en segundo plano para ejecutar las operaciones de larga duración. En este primer artículo de la serie nos vamos a centrar en dos de las alternativas más directas a la hora de ejecutar tareas en segundo plano en Android:
- Crear nosotros mismos de forma explícita un nuevo hilo para ejecutar nuestra tarea.
- Utilizar la clase auxiliar AsyncTask proporcionada por Android.
Mi idea inicial para este artículo era obviar la primera opción, ya que normalmente la segunda solución nos es más que suficiente, y además es mas sencilla y más limpia de implementar. Sin embargo, si no comentamos al menos de pasada la forma de crear “a mano” nuevos hilos y los problemas que surgen, quizá no se viera demasiado claro las ventajas que tiene utilizar las AsyncTask. Por tanto, finalmente voy a pasar muy rápidamente por la primera opción para después centrarnos un poco más en la segunda. Además, aprovechando el tema de la ejecución de tareas en segundo plano, vamos a ver también cómo utilizar un control (el ProgressBar) y un tipo de diálogo (el ProgressDialog) que no vimos en los primeros temas del curso dedicados a la interfaz de usuario.
Y para ir paso a paso, vamso a empezar por crear una aplicación de ejemplo en cuya actividad principal colocaremos un control ProgressBar (en mi caso llamado pbarProgreso) y un botón (btnSinHilos) que ejecute una tarea de larga duración. Para simular una operación de larga duración vamos a ayudarnos de un método auxiliar que lo único que haga sea esperar 1 segundo, mediante una llamada a Thread.sleep().
private void tareaLarga()
{
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
}
Haremos que nuestro botón ejecute este método 10 veces, de forma que nos quedará una ejecución de unos 10 segundos en total:
btnSinHilos.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
pbarProgreso.setMax(100);
pbarProgreso.setProgress(0);
for(int i=1; i<=10; i++) {
tareaLarga();
pbarProgreso.incrementProgressBy(10);
}
Toast.makeText(MainHilos.this, "Tarea finalizada!",
Toast.LENGTH_SHORT).show();
}
});
Como veis, aquí todavía no estamos utilizando nada especial, por lo que todo el código se ejecutará en el hilo principal de la aplicación. En cuanto a la utilización del control ProgressBar vemos que es muy sencilla y no requiere apenas configuración. En nuestro caso tan sólo hemos establecido el valor máximo que alcanzará (el valor en el que la barra de progreso estará rellena al máximo) mediante el método setMax(100), posteriormente la hemos inicializado al valor mínimo mediante una llamada a setProgress(0) de forma que inicialmente aparezca completamente vacía, y por último en cada iteración del bucle incrementamos su valor en 10 unidades llamando a incrementProgressBy(10), de tal forma que tras la décima iteración la barra llegue al valor máximo de 100 que establecimos antes. Finalmente mostramos un mensaje Toast para informar de la finalización de la tarea. Pues bien, ejecutemos la aplicación, pulsemos el botón, y veamos qué ocurre.
No era eso lo que esperábamos ¿verdad? Lo que ha ocurrido es que desde el momento que hemos pulsado el botón para ejecutar la tarea, hemos bloqueado completamente el resto de la aplicación, incluida la actualización de la interfaz de usuario, que ha debido esperar a que ésta termine mostrando directamente la barra de progreso completamente llena. En definitiva, no hemos sido capaces de ver el progreso de la tarea. Pero como decíamos, este efecto puede empeorar. Probemos ahora a pulsar el botón de la tarea y mientras ésta se ejecuta realicemos cualquier acción sobre la pantalla, un simple click sobre el fondo nos basta. Veamos qué ocurre ahora.
Vemos cómo al intentar hacer cualquier acción sobre la aplicación Android nos ha advertido con un mensaje de error que la aplicación no responde debido a que se está ejecutando una operación de larga duración en el hilo principal. El usuario debe elegir entre esperar a que termine de ejecutarla o forzar el cierre de la aplicación. Pues bien, estos son los efectos que vamos a intentar evitar. La opción más inmediata que nos proporciona Android, al igual que otras plataformas, es crear directamente hilos secundarios dentro de los cuales ejecutar nuestras operaciones costosas. Esto lo conseguimos en Android instanciando un objeto de la clase Thread. El constructor de la clase Thread recibe como parámetro un nuevo objeto Runnable que debemos construir implementando su método run(), dentro del cual vamos a realizar nuestra tarea de larga duración. Hecho esto, debemos llamar al método start() del objeto Threaddefinido para comenzar la ejecución de la tarea en segundo plano.
new Thread(new Runnable() {
public void run() {
//Aquí ejecutamos nuestras tareas costosas
}
}).start();
Hasta aquí todo sencillo y relativamente limpio. Los problemas aparecen cuando nos damos cuenta que desde este hilo secundario que hemos creado no podemos hacer referencia directa a componentes que se ejecuten en el hilo principal, entre ellos los controles que forman nuestra interfaz de usuario, es decir, que desde el método run() no podríamos ir actualizando directamente nuestra barra de progreso de la misma forma que lo hacíamos antes. Para solucionar esto, Android proporciona varias alternativas, entre ellas la utilización del método post() para actuar sobre cada control de la interfaz, o la llamada al método runOnUiThread() para “enviar” operaciones al hilo principal desde el hilo secundario [Nota: Sí, vale, sé que no he nombrado la opción de los Handler, pero no quería complicar más el tema por el momento]. Ambas opciones requieren como parámetro un nuevo objeto Runnable del que nuevamente habrá que implementar su método run() donde se actúe sobre los elementos de la interfaz. Por ver algún ejemplo, en nuestro caso hemos utilizado el método post() para actuar sobre el control ProgressBar, y el método runOnUiThread()para mostrar el mensaje toast.
new Thread(new Runnable() {
public void run() {
pbarProgreso.post(new Runnable() {
public void run() {
pbarProgreso.setProgress(0);
}
});
for(int i=1; i<=10; i++) {
tareaLarga();
pbarProgreso.post(new Runnable() {
public void run() {
pbarProgreso.incrementProgressBy(10);
}
});
}
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(MainHilos.this, "Tarea finalizada!",
Toast.LENGTH_SHORT).show();
}
});
}
}).start();
Utilicemos este código dentro de un nuevo botón de nuestra aplicación de ejemplo y vamos a probarlo en el emulador.
Ahora sí podemos ver el progreso de nuestra tarea reflejado en la barra de progreso. La creación de un hilo secundario nos ha permitido mantener el hilo principal libre de forma que nuestra interfaz de usuario de actualiza sin problemas durante la ejecución de la tarea en segundo plano. Sin embargo miremos de nuevo el código anterior. Complicado de leer, ¿verdad? Y eso considerando que tan sólo estamos actualizando un control de nuestra interfaz. Si el número de controles fuera mayor, o necesitáramos una mayor interacción con la interfaz el código empezaría a ser inmanejable, difícil de leer y mantener, y por tanto también más propenso a errores. Pues bien, aquí es donde Android llega en nuestra ayuda y nos ofrece la clase AsyncTask, que nos va a permitir realizar esto mismo pero con la ventaja de no tener que utilizar artefactos del tipo runOnUiThread() y de una forma mucho más organizada y legible. La forma básica de utilizar la clase AsyncTaskconsiste en crear una nueva clase que extienda de ella y sobrescribir varios de sus métodos entre los que repartiremos la funcionalidad de nuestra tarea. Estos métodos son los siguientes:
- onPreExecute(). Se ejecutará antes del código principal de nuestra tarea. Se suele utilizar para preparar la ejecución de la tarea, inicializar la interfaz, etc.
- doInBackground(). Contendrá el código principal de nuestra tarea.
- onProgressUpdate(). Se ejecutará cada vez que llamemos al método publishProgress() desde el método doInBackground().
- onPostExecute(). Se ejecutará cuando finalice nuestra tarea, o dicho de otra forma, tras la finalización del método doInBackground().
- onCancelled(). Se ejecutará cuando se cancele la ejecución de la tarea antes de su finalización normal.
Estos métodos tienen una particularidad esencial para nuestros intereses. El método doInBackground() se ejecuta en un hilo secundario (por tanto no podremos interactuar con la interfaz), pero sin embargo todos los demás se ejecutan en el hilo principal, lo que quiere decir que dentro de ellos podremos hacer referencia directa a nuestros controles de usuario para actualizar la interfaz. Por su parte, dentro de doInBackground() tendremos la posibilidad de llamar periódicamente al método publishProgress() para que automáticamente desde el método onProgressUpdate() se actualice la interfaz si es necesario. Al extender una nueva clase de AsyncTaskindicaremos tres parámetros de tipo:
- El tipo de datos que recibiremos como entrada de la tarea en el método doInBackground().
- El tipo de datos con el que actualizaremos el progreso de la tarea, y que recibiremos como parámetro del método onProgressUpdate() y que a su vez tendremos que incluir como parámetro del método publishProgress().
- El tipo de datos que devolveremos como resultado de nuestra tarea, que será el tipo de retorno del método doInBackground() y el tipo del parámetro recibido en el método onPostExecute().
En nuestro caso de ejemplo, extenderemos de AsyncTask indicando los tipos Void, Integer y Booleanrespectivamente, lo que se traducirá en que:
- doInBackground() no recibirá ningún parámetro de entrada (Void).
- publishProgress() y onProgressUpdate() recibirán como parámetros datos de tipo entero (Integer).
- doInBackground() devolverá como retorno un dato de tipo booleano y onPostExecute() también recibirá como parámetro un dato del dicho tipo (Boolean).
Dicho esto, cómo repartiremos la funcionalidad de nuestra tarea entre los distintos métodos. Pues sencillo, en onPreExecute() inicializaremos la barra de progreso estableciendo su valor máximo y poniéndola a cero para comenzar. En doInBackground() ejecutaremos nuestro bucle habitual llamando a publishProgress() tras cada iteración indicando el progreso actual. En onProgressUpdate() actualizaremos el estado de la barra de progreso con el valor recibido como parámetro. y por último en onPostExecute() mostraremos el mensaje Toast de finalización de la tarea. Veamos el código completo:
private class MiTareaAsincrona extends AsyncTask {
@Override
protected Boolean doInBackground(Void... params) {
for(int i=1; i<=10; i++) {
tareaLarga();
publishProgress(i*10);
if(isCancelled())
break;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
int progreso = values[0].intValue();
pbarProgreso.setProgress(progreso);
}
@Override
protected void onPreExecute() {
pbarProgreso.setMax(100);
pbarProgreso.setProgress(0);
}
@Override
protected void onPostExecute(Boolean result) {
if(result)
Toast.makeText(MainHilos.this, "Tarea finalizada!",
Toast.LENGTH_SHORT).show();
}
@Override
protected void onCancelled() {
Toast.makeText(MainHilos.this, "Tarea cancelada!",
Toast.LENGTH_SHORT).show();
}
}
Si observamos con detenimiento el código, la única novedad que hemos introducido es la posibilidad de cancelar la tarea en medio de su ejecución. Esto se realiza llamando al método cancel() de nuestra AsyncTask (para lo cual añadiremos un botón más a nuestra aplicación de ejemplo, además del nuevo que añadiremos para comenzar la tarea). Dentro de la ejecución de nuestra tarea en doInBackground() tendremos además que consultar periodicamente el resultado del método isCancelled() que nos dirá si el usuario ha cancelado la tarea (es decir, si se ha llamado al método cancel()), en cuyo caso deberemos de terminar la ejecución lo antes posible, en nuestro caso de ejemplo simplemente saldremos del bucle con la instrucción break. Además, tendremos en cuenta que en los casos que se cancela la tarea, tras el método doInBackground() no se llamará a onPostExecute() sino al método onCancelled(), dentro del cual podremos realizar cualquier acción para confirma la cancelación de la tarea. En nuestro caso mostraremos un mensaje Toast informando de ello.
Mucho mejor que las alternativas anteriores, verdad? Pero vamos a mostrar una opción más. Si queremos que el usuario pueda ver el progreso de nuestra tarea en segundo plano, pero no queremos que interactúe mientras tanto con la aplicación tenemos la opción de mostrar la barra de progreso dentro de un diálogo. Android nos proporciona directamente un componente de este tipo en forma de un tipo especial de diálogo llamado ProgressDialog.
Configurar un cuadro de diálogo de este tipo es muy sencillo. Para ello vamos a añadir un botón más a nuestra aplicación de ejemplo, donde inicializaremos el diálogo y lanzaremos la tarea en segundo plano. Para inicializar el diálogo comenzaremos por crear un nuevo objeto ProgressDialog pasándole como parámetro el contexto actual. Tras esto estableceremos su estilo: STYLE_HORIZONTAL para una barra de progreso tradicional, o STYLE_SPINNER para un indicador de progreso de tipo indeterminado.
Lo siguiente será especificar el texto a mostrar en el diálogo, en nuestro caso el mensaje “Procesando…“, y el valor máximo de nuestro progreso, que lo mantendremos en 100. Por último indicaremos si deseamos que el diálogo sea cancelable, es decir, que el usuario pueda cerrarlo pulsando el botón Atrás del teléfono. Para nuestro ejemplo activaremos esta propiedad para ver cómo podemos cancelar también nuestra tarea en segundo plano cuando el usuario cierra el diálogo. Tras la configuración del diálogo lanzaremos la AsyncTask del ejemplo anterior, que tendremos que modificar ligeramente para adaptarla al nuevo diálogo. Veamos el código por ahora:
btnAsyncDialog.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
pDialog = new ProgressDialog(MainHilos.this);
pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pDialog.setMessage("Procesando...");
pDialog.setCancelable(true);
pDialog.setMax(100);
tarea2 = new MiTareaAsincronaDialog();
tarea2.execute();
}
});
La AsyncTask será muy similar a la que ya implementamos. De hecho el método doInBackground() no sufrirá cambios.
En onProgressUpdate() la única diferencia será que actualizaremos el progreso llamando al método setProgress() del ProgressDialog en vez de la ProgressBar.
El código de onPreExecute() sí tendrá algún cambio más. Aprovecharemos este método para implementar el evento onCancel del diálogo, dentro del cual cancelaremos también la tarea en segundo plano llamando al método cancel(). Además, inicializaremos el progreso del diálogo a 0 y lo mostraremos al usuario mediante el método show().
Por último, en el método onPostExecute() además de mostrar el Toast de finalización, tendremos que cerrar previamente el diálogo llamando a su método dismiss().
Veamos el código completo de la AsyncTask modificada para usar el nuevo ProgressDialog.
private class MiTareaAsincronaDialog extends AsyncTask {
@Override
protected Boolean doInBackground(Void... params) {
for(int i=1; i<=10; i++) {
tareaLarga();
publishProgress(i*10);
if(isCancelled())
break;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
int progreso = values[0].intValue();
pDialog.setProgress(progreso);
}
@Override
protected void onPreExecute() {
pDialog.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
MiTareaAsincronaDialog.this.cancel(true);
}
});
pDialog.setProgress(0);
pDialog.show();
}
@Override
protected void onPostExecute(Boolean result) {
if(result)
{
pDialog.dismiss();
Toast.makeText(MainHilos.this, "Tarea finalizada!",
Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onCancelled() {
Toast.makeText(MainHilos.this, "Tarea cancelada!",
Toast.LENGTH_SHORT).show();
}
}
Si ahora ejecutamos nuestro proyecto y pulsamos sobre el último botón incluido veremos cómo el diálogo aparece por encima de nuestra actividad mostrando el progreso de la tarea asíncrona. Tras finalizar, el diálogo desaparece y se muestra el mensaje toast de finalización. Si en cambio, se pulsa el botón Atrás del dispositivo antes de que la tarea termine el diálogo se cerrará y se mostrará el mensaje de cancelación.
Y con esto habríamos concluido este primer artículo sobre hilos y tareas en segundo plano. Os dejo a continuación el vídeo de demostración de la aplicación de ejemplo construida durante el tema, y como siempre el código fuente completo del ejemplo.



Me encanta tu tutorial y la forma en que lo explicas todo, enhorabuena ;)
Ahora una pequeña cuestión que me trae de cabeza :P Estoy trabajando con la versión 2.1 y utilizo kSoap, y me enterado que no funciona bien en las versiones superiores (4.0) porque cada llamada a Soap se debe hacer en un hilo secundario, entonces mi pregunta es la siguiente: ¿Por cada llamada Soap que realice, tengo que crear una clase AsyncTask?
Muchas gracias
Hola Alvaro, no es necesario crear una clase que extienda de AsyncTask por cada llamada al servicio web. Como has visto en este artículo tu clase AsyncTask la puedes parametrizar para que reciba distintos datos de entrada, salida y progreso, por lo que podrías crearte una AsyncTask suficientemente genérica como para que te sirva para distintos tipos de llamada al servicio. Saludos.
Gracias por hacer este tutorial!!
Muchas gracias!! Probaré a hacerlo como dices a ver si consigo resolver mi problema :D
Tengo otra pregunta, pero no es referida al tema del AsyncTask, sino a los AlertDialog, pero nadie es capaz de responderme (ni en el foro nuestro, ni buscando por internet).
Me gustaría poder poner un boton que al darle se te abra un AlertDialog (con sus botones de aceptar y cancelar) y en vez de texto tubiera un RatingBar para poder votar.
Muchas gracias por todo de nuevo compañeros :D
Álvaro, te he contestado en el foro al tema del AlertDialog.
Muchas gracias por todo Sgoliver!! Sin tu ayuda se me habría hecho muy cuesta arriba el comenzar mi Proyecto fin de carrera.
:D
MUCHISIMAS GRACIAS!!!!!!!!!
Estoy desorrollando una aplicacion y toda la informacion q compartes en tu blog me ha servido para acabarla.
sigue adelante.
y otra vez gracias
Una duda que tengo… donde puedo comprobar si el hilo ha acabado para empezar uno nuevo? He probado con esto..
hilo = new MiHiloAsincrono();
hilo.execute();
if(hilo.getStatus() == AsyncTask.Status.FINISHED)
{
Log.i(“Acabado”,”");
}
Pero no me refleja nada. No se como tendria que comprobarlo.
Gracias de antemano.
@LuTOMetal el AsyncTask tiene dos formas de terminar, como ha dicho Salvador:
1. que termine normalmente -> se ejecuta onPostExecute()
2. que sea cancelado por el usuario -> se ejecuta onCancelled()
en ambos casos podrías poner Log.i(“Acabado”,”")
cualquier otra solución implicaría bloquear el hilo principal (se podría hacer con un while en lugar del if que tu has puesto, pero eso haría que se bloquease)
la razón de que tu código no te refleje nada es porque justo después de que ejecutas el hilo (y éste sigue corriendo) haces la comprobación, que lógicamente da falso y por tanto no te logea nada
espero ser de ayuda :)
@Marc muchas gracias por tu respuesta :)
Hola
Fabuloso blog antes que nada. No sé si esta pregunta está especialmente relacionada con esto, pero creo que al menos es un tema parecido, ahí va:
¿Cómo puedo saber si mi aplicación ya está abierta para que cuando alguien vuelve a darle al icono recupere su estado anterior y no vuelva a abrirse sola? ¿Dejando un proceso en segundo plano? ¿Y cómo compruebo si ya se está ejecutando?
Es que estoy haciendo una aplicación para una radio online. Algo muy simple, un objeto MediaPlayer que establece la fuente y reproduce. Pero si alguien le da a menú y pulsa de nuevo la aplicación parece que se vuelve algo loca porque ya no se cierra el proceso anterior.
Espero que me puedas ayudar y enhorabuena de nuevo por tu blog.
José Manuel.
Estimado amigo, la actualización de la barra de tareas con esta tarea tan sencilla es fácil, pero cuando se está realizando un tarea como por ejemplo subir datos de un servidor, ¿como actualizas la barra durante dicho trabajo? En el ejemplo la tarea dura un segundo, pero en una tarea como la que indico no se sabe el tiempo que va a ocupar. ¿Como podría hacerse para que dentro de dicha tarea actualizase la barra cada cierto tiempo?
Hola quiero insertar en una base de datos local mientras estoy en la tarea, pero cuando quiero instanciar la base y me pide el contexto no me deja pasarle el task como contexto.
Como lo puedo resolver, desde ya muchas gracias.
Hola
Soy novato en android y necesito saber donde debo hacer la implementación de la clase que extiende de AsyncTask, si en la misma activity que la clase principal o en una nueva activity.
de antemano muchas gracias.
Estoy recibiendo clases de Android y me parece que esta forma de explicar es muchisimo mejor de las que encuentro en Ingles. Gracias.
Tengo un spinner. En la tarea BG leo un archivo de un website para cargar el ArrayAdapter del spinner. El proceso del BG lo realiza perfecto asi como la carga del spinner. Pero no tengo respuesta del spinner cuando selecciono un elemento. si retiro la tarea del BG y utilizo la misma logica pero con un archivo en el directorio raw, funciona perfectamente. Que debo modificar para que se ejecute el listener del spinner
Hola! estoy pasando un programa java a android y tengo una duda sobre la sincronizacion:
El programa es un gestor de entradas (no tiene fin practico, solo es para aprender), primero lo hice en consola y luego lo pase a gui, pero para hacerlo tuve que implementar una clase synchronized para que coordinara el programa principal y que cuando requiriera una entrada de texto parara ( wait() ) hasta que se introdujera texto y se pulsara un boton o se pulsara enter ( notify() ).
Esto en java me funciona, pero en android no… No he encontrado mucha informacion (practicamente ninguna) sobre wait()/notify() en android, no se si es que se usa otra clase nativa de android… ¿sabes algo de esto?
Gracias
Hola, excelente tu tutorial pero tengo una duda espero me puedas guiar, tengo varias clases todas estan englobadas en una sola por medio de un tabHost, cree un boton en la clase principal que es donde tengo los tabHost y por estar en esa clase al abrir las otras clases por medio de sus tabs en estas se muestra el boton que te menciono, el problema que tengo es que al presionar el boton sin importar la clase que tenga abierta pueda recopilar la informacion que tengo alamacenada en un arreglos y pasarlos a unos archivos, mi problema es que al estar el boton declarado en la clase principal al abrir otra clase que no sea la principal y presionar el boton el oyente del boton no se ejecuta y si lo presiono estando en la principal si se ejecuta pero no puede acceder a los datos de las otras clases, entonces se me ocurrio usar hilos uno en cada clase que estaran esperando a que el boton se presionado y a si poder guardar su informacion al archivo, quisiera saber si esto es posible o existe alguna otra forma de poder compartir el boton y los datos de las clases. espero me puedas ayudar, gracias.
Primero agradecerte toda la informacion que das en este blog.
Estoy intentando aplicar AsyncTask al ejemplo de SAX que tienes, pero en la clase que tu aquí llamas MiTareaAsincronaDialog no me reconoce las variables de mi Actividad ni siquiera el nombre de ella al intentar sacar el dialogo, me da un error donde tu pones MainHilos.
public class ObtenerFotos extends AsyncTask {
protected List doInBackground(Void… params) {
// TODO Auto-generated method stub
noticias = saxparser.parse();
return noticias;
}
@Override
protected void onPreExecute() {
RssParserSax saxparser = new RssParserSax(“http://www.europapress.es/rss/rss.aspx”);
List noticias;
}
protected void onPostExecute(List result) {
dialogo.dismiss();//lo que en el codigo llamas pbarProgreso
Toast.makeText(FotosActivity.this, “Tarea finalizada!”, //FotosActivity me da error
Toast.LENGTH_SHORT).show();
}
Estoy haciendo una barbaridad? Donde estoy fallando?
Muchas gracias por anticipado.
Ya está todo solucionado. Ya me funciona y por supuesto gracias a tu curso.
He ido recopilando información de un lado y de otro y lo he conseguido.
Así que vuelvo a darte las gracias por lo buena y util que es tu página.
Gracias.
Excelente!! Tengo una pregunta para ti, OJALÁ me puedas ayudar (soy nuevo en android)… Mi AsyncTask trata de acceder a una API para comprobar la existencia de un nombre de usuario (restful web service).
Bien, he notado que tras la ejecución de un AsyncTask no se ejecuta nada posteriormente (de hecho en mi código tras comprobar el username compruebo varias cosas más). ¿Hay algo que pueda hacer?
Gracias amigo (mi correo el captain06@gmail.com)
Nada amigo!!! Ya lo he solucionado :) gracias por el post !!
¡Muchas gracias por el tutorial!, me ha sido de gran utilidad.
Un nuevo tutorial sencillo, gracias por estos aportes!