Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Android avanzado Sesión 1: Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Puntos a tratar • • • • • • • Hilos Hilos de ejecución Hilos y actividades Finalización y pausa Sincronización de hilos Handlers, Loopers, Pools de hilos Hilos e interfaz de usuario AsyncTask © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 2 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Hilo de ejecución (Thread) • • • • Ejecutar una tarea en segundo plano Actualizar periódicamente algún dato Garantizar la fluidez de la interfaz gráfica Operaciones de entrada y salida, comunicaciones • Threads: objetos con método run( ) Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 3 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Creación y ejecución • Por herencia de Thread: public class Hilo1 extends Thread { @Override public void run() { while(condicion_de_ejecucion){ //Realizar operaciones //... try { // Dejar libre la CPU durante unos milisegundos Thread.sleep(100); } catch (InterruptedException e) { return; } } } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 4 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Creación y ejecución • Por implementación de Runnable: public class Hilo2 implements Runnable { @Override public void run() { while(condicion_de_ejecucion){ //Realizar operaciones //... try { // Dejar libre la CPU durante unos milisegundos Thread.sleep(100); } catch (InterruptedException e) { return; } } } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 5 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Creación y ejecución • Diferentes formas de ejecución según la clase: Hilo1 hilo1 = new Hilo1(); hilo1.start(); Thread hilo2 = new Thread(new Hilo2()); hilo2.start(); • Declaración de la clase inline: new Thread(new Runnable() { public void run() { //Realizar operaciones ... } }).start(); Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 6 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Creación y ejecución • Diferentes formas de ejecución según la clase: Hilo1 hilo1 = new Hilo1(); hilo1.start(); Thread hilo2 = new Thread(new Hilo2()); hilo2.start(); • Declaración de la clase inline: new Thread(new Runnable() { public void run() { //Realizar operaciones ... } }).start(); Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 7 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Acceso a datos de la aplicación • Si el hilo necesita acceder a datos de la aplicación podemos pasárselos a través del constructor @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); new Hilo1(getApplicationContext()); } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 8 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Acceso a datos de la aplicación public class Hilo1 extends Thread { Context context; public Hilo2Thread(Context context){ this.context = context; this.start(); } @Override public void run() { while(condicion_de_ejecucion){ //Realizar operaciones //... try { // Dejar libre la CPU durante unos milisegundos Thread.sleep(100); } catch (InterruptedException e) { return; } } } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 9 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Ciclo de vida Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 10 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Ciclo de vida • New: El primer estado de un hilo recién creado. Permanece en este estado hasta que el hilo es ejecutado. • Runnable: Una vez ejecutado pasa a este estado, durante el cuál ejecuta su tarea. • Not runnable: Estado que permite al hilo desocupar la CPU en espera a que otro hilo termine o le notifique que puede continuar, o bien a que termine un proceso de E/S, o bien a que termine una espera provocada por la función Thread.sleep(100);. Tras ello volverá al estado Runnable. • Dead: Pasa a este estado una vez finalizado el método run(). Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 11 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Finalización • Es muy importante asegurar que un hilo saldrá de la función run() cuando sea necesario, para que pase al estado dead. • Ejemplo: establecer una variable booleana como condición de salida. • No hay un método stop( ) (está deprecated, por tanto no hay que usarlo), en su lugar el programador debe programar el mecanismo que termine de forma interna la ejecución del hilo. • Si un hilo se encuentra en estado Not Runnable, no podrá hacer el return de su función run( ) hasta que no vuelva al estado Runnable. Una forma de interrumpirlo es usar el método Thread.interrupt( );. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 12 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Hilos Daemon • En Android cada aplicación se ejecuta en su propia máquina virtual. Ésta no terminará mientras haya hilos en ejecución que no sean de tipo daemon. • Para hacer que un hilo sea daemon se utiliza la función: hilo1.setDaemon(true); hilo1.start(); • Aún así es recomendable salir de la función run() cuando la lógica del programa lo considere oportuno. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 13 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Actividades que implementan Runnable • Para facilitar el acceso a los campos de la Activity: public class HiloActivity extends Activity implements Runnable{ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //Iniciar el segundo hilo de ejecución: (new Thread(this)).start(); } @Override public void run() { while(condicion_de_ejecucion){ //Realizar operaciones //... } } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 14 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Actividades que implementan Runnable • Nótese que en el ejemplo anterior se podrían crear más hilos de ejecución creando más instancias del hilo e iniciándolas con (new Thread(this)).start(); pero compartirían los mismos datos correspondientes a los campos de la clase HiloActivity. • Al cerrar una Activity los hilos secundarios no terminan. Al pausarla tampoco se pausan. El programador debe encargarse de eso. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 15 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Actividades en pausa • Por defecto para cerrar una actividad hay que pulsar el botón hacia atrás del dispositivo Android. • Si se pulsa la tecla Home o se recibe una llamada de teléfono la actividad pasa a estado pausado. • Al pasar a pausa habría que pausar los threads desde el método onPause() y reestablecerlos en onResume(), con los mecanismos de sincronización wait y notifyAll. • Otra fórmula aplicable sería terminar los hilos y volver a crearlos al recuperarse del estado de pausa de la actividad. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 16 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Prioridades • Cada hilo tiene asociada una prioridad que se puede cambiar con el método: Thread.setPriority(prioridad) • El parámetro es un valor entero entre Thread.MIN_PRIORITY (que vale 1) y Thread.MAX_PRIORITY (que vale 10). • El efecto de la prioridad de los hilos sólo puede llegar a apreciarse si varios hilos están simultáneamente en estado "Running". Si el hilo tiene estados "Waiting", o bien para liberar CPU, o bien porque esperan E/S, o bien porque esperan otro hilo, en estos intervalos de tiempo el procesador estaría liberado para ejecutar el otro hilo, independientemente de su prioridad. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 17 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Sincronización de hilos • • • • Hilos Para acceder a datos compartidos sin corromperlos Para esperar la obtención de un recurso Para esperar a otro hilo Para liberar a otro hilo de la espera © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 18 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Acceso a datos compartidos • Si los datos son sincronizados, por ejemplo la versión serializada de List que es Vector, el acceso desde varios hilos no es problemático. • De lo contrario hay que sincronizar el método. Hay dos maneras: public synchronized void incrementa(int incremento){ this.valor += incremento; } public synchronized void incrementa(int incremento){ synchronized(this){ this.valor += incremento; } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 19 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Esperar a otro hilo • Métodos que permiten bloquear un hilo de manera explícita y desbloquearlo cuando sea necesario: wait( ) y notify( ), o notifyAll( ) • Para evitar interbloqueos ambos métodos deben ser llamados siempre desde secciones de código sincronizadas. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 20 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Esperar a otro hilo public class CubbyHole { private Object cubby; //El productor llamaría a este método. public synchronized Object produce(Object data) { Object ret = cubby; cubby = data; // Desbloquear el consumidor que esté esperando en consume(). notifyAll(); return ret; } ... Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 21 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Esperar a otro hilo ... //El consumidor llamaría a este método public synchronized Object consume() throws InterruptedException { // Bloquear hasta que exista un objeto que consumir while (cubby == null) { // Libera el lock thel objeto mientras espera y // lo cierra al seguir ejecutándose wait(); } Object ret = cubby; cubby = null; return ret; } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 22 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Esperar a que un hilo finalice • Con el método Thread.join( ) Thread descarga1 = new Thread(new DescargaRunnable(url1)); Thread descarga2 = new Thread(new DescargaRunnable(url2)); //Bloquear hasta que ambas se descarguen descarga1.join(); descarga2.join(); //Descargadas, realizar alguna operación con los datos Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 23 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Pausar un hilo • Usando una variable booleana que así lo indique y el método wait( ), quedando a la espera de un notifyAll( ) class MiRunnable implements Runnable { private Object pauseLock; private boolean paused; private boolean finished; public MiRunnable() { pauseLock = new Object(); paused = false; finished = false; } ... Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 24 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Pausar un hilo ... public void run() { while (!finished) { // Realizar operaciones // ... synchronized (pauseLock) { while (paused) { try { pauseLock.wait(); } catch (InterruptedException e) { break; } } } } } ... Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 25 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Pausar un hilo ... //Llamarlo desde Activity.onPause() public void pause() { synchronized (pauseLock) { paused = true; } } //Llamarlo desde Activity.onResume() public void resume() { synchronized (pauseLock) { paused = false; pauseLock.notifyAll(); } } //Llamarlo desde Activity.onDestroy() public void finish() { finished = true; } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 26 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Mecanismos específicos de Android • Android cuenta con un framework de mensajería y concurrencia que está basado en las clases Thread, Looper, Message, MessageQueue y Handler. • Por conveniencia existe también la clase AsyncTask, para usos comunes de actualización de la UI. • Un Looper se utiliza para ejecutar un bucle de comunicación por mensajes en determinado hilo. • La interacción con el Looper se realiza a través de la clase Handler, que permite enviar y procesar un Message y objetos que implementen Runnable, asociados a una cola MessageQueue. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 27 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Mensajes mediante Handlers • Handler permite enviar y procesar un Message y objetos que implementen Runnable. • Cada instancia de Handler se asocia con un único hilo y su cola de mensajes. • Hay que crear el Handler desde el mismo hilo al que debe ir asociado. • Un hilo puede tener asociados varios Handler. • Mediante el uso de Handler se pueden encolar una acciones que que deben ser realizadas en otro hilo de ejecución distinto. Esto permite programar acciones que deben ser ejecutadas en el futuro, en un orden determinado. • El envío se realiza mediante post(Runnable) o postDelayed(Runnable, long), donde se indican los milisegundos de retardo para realizar el post. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 28 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Mensajes mediante Handlers • Ejemplo de tareas que se ejecutan una después de la otra, en el mismo hilo de ejecución: public class Actividad extends Activity { TextView textView; Handler handler; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textView = (TextView) findViewById(R.id.textview1); handler = new Handler(); handler.removeCallbacks(tarea1); handler.removeCallbacks(tarea2); handler.post(tarea1); handler.post(tarea2); } ... Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 29 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Handler: ejemplo de splashscreen public class Actividad extends Activity implements Runnable{ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.splashscreen); (new Thread(this)).start(); } private Handler handler = new Handler(){ public void handleMessage(Message msg){ setContentView(R.layout.main); } }; @Override public void run() { //Carga lenta de datos... try { Thread.sleep(2000); } catch (InterruptedException e) { } handler.sendEmptyMessage(0); } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 30 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Looper • Looper se utiliza para ejecutar un bucle de comunicación por mensajes en determinado hilo. • Por defecto los hilos no tienen ningún bucle de mensajes asociado. Para crearlo hay que llamar a Looper.prepare() desde el hilo correspondiente. • En el siguiente ejemplo se implementa un hilo que cuenta con un método post(Runnable) para encolar las tareas al handler. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 31 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Looper class MiHiloLooper extends Thread{ Handler handler; //message handler public MiHiloLooper(){ this.start(); } @Override public void run(){ try{ Looper.prepare(); handler = new Handler(); Looper.loop(); }catch(Throwable t){ Log.e("Looper","Error: ", t); } } public void terminate(){ handler.getLooper().quit(); } public void post(Runnable runnable){ handler.post(runnable); } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 32 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Looper • Se usaría instanciándolo y pasando tareas Runnable por post( ) cada vez que haga falta. • Es útil para casos en los que no se sepa en qué orden irán encolándose las tareas necesarias. • Ejemplo: descarga de múltiples archivos uno a uno, para evitar numerosas conexiones simultáneas MiHiloLooper looper = new MiHiloLooper(); looper.post(new Thread( new ImageLoader(url1, iv1) )); looper.post(new Thread( new ImageLoader(url2, iv2) )); looper.post(new Thread( new ImageLoader(url3, iv3) )); Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 33 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Pools de hilos • Encolar tareas especificando número simultáneo de hilos • número mínimo de hilos a mantener en el pool, incluso aunque no estén en ejecución • número máximo de hilos permitido • el tiempo que se permite que permanezca un hilo que ya no esté en ejecución y la unidad de tiempo • la cola de tareas. ThreadPoolExecutor pool; ArrayBlockingQueue<Runnable> queue; queue = new ArrayBlockingQueue<Runnable>(5); pool = new ThreadPoolExecutor( poolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, queue); pool.execute(new MyRunnable("A")); pool.execute(new MyRunnable("B")); pool.execute(new MyRunnable("C")); pool.execute(new MyRunnable("D")); pool.execute(new MyRunnable("E")); Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 34 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Operaciones lentas e interfaz de usuario • En dispositivos móviles no se puede asumir que ninguna operación de red vaya a ser rápida. • En android las operaciones lentas se realizan en un hilo aparte, de lo contrario obtendremos la pantalla ARN, Application not responding. • El hilo de la actividad se encarga de mantener operativa la interfaz de usuario. Si la interfaz deja de ser operativa durante 2 segundos, Android sugiere terminar la aplicación con la ARN Screen. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 35 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Application Not Responding • • • • Hilos El usuario normalmente no esperará. Pondrá comentario negativo. Desinstalará la aplicación. No volverá a instalar ninguna versión posterior. © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 36 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Hilo único para la GUI • Crear otro hilo de ejecución es fácil, pero no nos permite acceder a componentes gráficos: modelo de hilo único para la interfaz gráfica ImageView imageView = (ImageView)findViewById(R.id.ImageView01); new Thread(new Runnable() { public void run() { Drawable imagen = cargarLaImagen("http://..."); //Desde aquí NO debo acceder a imageView } }).start(); Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 37 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Enviar datos a la interfaz • • • • • Hilos Activity.runOnUiThread(Runnable) View.post(Runnable) View.postDelayed(Runnable, long) Handler AsyncTask © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 38 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles Enviar datos a la interfaz • Una solución es utilizar el método View.post(Runnable) ImageView imageView = (ImageView)findViewById(R.id.ImageView01); new Thread(new Runnable() { public void run() { Drawable imagen = cargarLaImagen("http://..."); imageView.post(new Runnable() { public void run() { imageView.setDrawable(imagen); } }); } }).start(); Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 39 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles AsyncTask • Clase diseñada para facilitar el trabajo con hilos e interfaz gráfica • Facilita la separación entre • Tarea secundaria • Interfaz gráfica • Permite solicitar refresco del progreso desde la tarea secundaria • El refresco, tras ser solicitado, se ejecutará (en algún momento) en el hilo principal Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 40 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles AsyncTask • Crear una clase que herede de AsyncTask • Sobrecargar como mínimo el método doInBackground(Params...) • Ejecutar una instancia de la clase con el método .execute(...) • Otros métodos que podemos sobrecargar • • • • • Hilos onCancelled() onPostExecute(Result) onPreExecute() onProgressUpdate(Progress... ) publishProgress(Progress... ) © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 41 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles AsyncTask • La ejecutamos con .execute(...) • Se invoca onPreExecute() en el hilo de la interfaz. • Se invoca doInBackground(Params...) en el segundo hilo • Puede realizar llamadas a publishProgress(Progress...) • Los valores se publicarán en el hilo principal con publishProgress(Progress...) • Se invoca onPostExecute(Result) en el hilo de la interfaz. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 42 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles AsyncTask • Restricciones de uso • La instancia de la AsyncTask debe ser creada en el hilo de la interfaz. • El método execute(Params...) debe ser invocado en el hilo de la interfaz. • No se deben llamar onPreExecute(), onPostExecute(), doInBackground(Params...) y onProgressUpdate(Progress...) manualmente. • La AsyncTask sólo se puede ejecutar una vez. Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 43 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles AsyncTask • TextView con el progreso y varios ImageView en los que queremos cargar imágenes con nuestra BajarImagenesTask. TextView textView; ImageView[] imageView; public void bajarImagenes(){ textView = (TextView)findViewById(R.id.TextView01); imageView[0] = (ImageView)findViewById(R.id.ImageView01); imageView[1] = (ImageView)findViewById(R.id.ImageView02); imageView[2] = (ImageView)findViewById(R.id.ImageView03); imageView[3] = (ImageView)findViewById(R.id.ImageView04); new BajarImagenesTask().execute( "http://a.com/1.png", "http://a.com/2.png", "http://a.com/3.png", "http://a.com/4.png"); } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 44 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles AsyncTask • TextView con el progreso y varios ImageView en los que queremos cargar imágenes con nuestra BajarImagenesTask. TextView textView; ImageView[] imageView; public void bajarImagenes(){ textView = (TextView)findViewById(R.id.TextView01); imageView[0] = (ImageView)findViewById(R.id.ImageView01); imageView[1] = (ImageView)findViewById(R.id.ImageView02); imageView[2] = (ImageView)findViewById(R.id.ImageView03); imageView[3] = (ImageView)findViewById(R.id.ImageView04); new BajarImagenesTask().execute( "http://a.com/1.png", "http://a.com/2.png", "http://a.com/3.png", "http://a.com/4.png"); } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 45 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles AsyncTask private class BajarImagenesTask extends AsyncTask<String, Integer, List<Drawable>> { @Override protected List<Drawable> doInBackground(String... urls) { ArrayList<Drawable> imagenes = new ArrayList<Drawable>(); for(int i=1;i<urls.length; i++){ cargarLaImagen(urls[0]); publishProgress(i); return imagenes; } @Override protected void onPreExecute() { super.onPreExecute(); textView.setText("Cargando imagenes..."); } @Override protected void onProgressUpdate(String... values) { textView.setText(values[0] + " imagenes cargadas..."); } @Override protected void onPostExecute(List<Drawable> result) { for(int i=0; i<result.length; i++){ imageView[i].setDrawable(result.getItemAt(i)); } textView.setText("Descarga finalizada"); } @Override protected void onCancelled() { textView.setText("Cancelada la descarga"); } } Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 46 Especialista Universitario en Desarrollo de Aplicaciones para Dispositivos Móviles ¿Preguntas...? Hilos © 2011-2012 Depto. Ciencia de la Computación e IA Android avanzado - 47