2010-04-25 17 views
8

Me encuentro con una IllegalStateException actualizando una Lista subyacente a un Adaptador (podría ser un ArrayAdapter o una extensión de BaseAdapter, no lo recuerdo). No tengo ni recuerdo el texto de la excepción en este momento, pero dice algo sobre el cambio del contenido de la Lista sin que se haya notificado el cambio al Adaptador.¿Cuál es la mejor manera de actualizar los datos subyacentes de un adaptador?

Esta lista/puede/ser actualizada a partir de otro hilo que no sea el de la interfaz de usuario (principal). Después de actualizar esta lista (agregando un elemento), llamo notifyDataSetChanged. El problema parece ser que el Adaptador o ListView conectado al Adaptador intenta actualizarse antes de invocar este método. Cuando esto sucede, se lanza la IllegalStateException.

Si configuro la visibilidad de ListView en GONE antes de la actualización, entonces VISIBLE nuevamente, no se produce ningún error. Pero esto no siempre es práctico.

Leí en alguna parte que no se puede modificar el subyacente de otro hilo - esto parece limitar un patrón MVC, como con esta Lista en particular, quiero agregar elementos de diferentes hilos. Supuse que siempre que llamara notifyDataSetChanged() estaría seguro, que el Adaptador no revisó la Lista subyacente hasta que se invocó este método, pero este no parece ser el caso.

Supongo que lo que estoy preguntando es, ¿puede ser seguro actualizar la Lista subyacente de hilos que no sean la UI? Además, si deseo modificar los datos dentro de un Adaptador, ¿puedo modificar la Lista subyacente o el Adaptador mismo (a través de sus métodos add(), etc.)? La modificación de los datos a través del adaptador parece incorrecta.

Encontré un hilo en otro sitio de alguien que parece tener un problema similar al mío: http://osdir.com/ml/Android-Developers/2010-04/msg01199.html (esto es de donde tomé la idea Visibility.GONE y .VISIBLE).

Para darle una mejor idea de mi problema en particular, describiré un poco cómo mi Lista, Adaptador, etc. están configurados.

Tengo un objeto llamado Queue que contiene una LinkedList. Queue extiende Observable, y cuando las cosas se agregan a su lista interna a través de sus métodos, yo llamo a setChanged() y notifyListeners(). Este objeto Queue puede tener elementos agregados o eliminados de cualquier cantidad de subprocesos.

Tengo una única actividad "vista de cola" que contiene un adaptador. Esta actividad, en su método onCreate(), registra un escucha Observer en mi objeto Queue. En el método de actualización del Observador(), invoco notifyDataSetChanged() en el Adaptador.

Agregué una gran cantidad de salida de registro y determiné que cuando se produce esta IllegalStateExcption que mi Observer de devolución de llamada nunca fue invocada. De modo que es como si el Adaptador notara el cambio de la Lista antes de que el Observador tuviera la oportunidad de notificar a sus Observadores, y llama a mi método para notificar al Adaptador que el contenido había cambiado.

Supongo que lo que estoy preguntando es si esta es una buena forma de instalar un Adaptador. ¿Es esto un problema porque estoy actualizando el contenido del adaptador de un hilo que no sea el de la interfaz de usuario? Si este es el caso, puedo tener una solución en mente (darle al objeto Queue un Handler al hilo de UI cuando se crea, y hacer todas las modificaciones de List usando ese Handler, pero esto parece inapropiado).

Me doy cuenta de que esta es una publicación muy abierta, pero estoy un poco perdido en esto y agradecería cualquier comentario sobre lo que he escrito.

Respuesta

8

Esta lista/Mayo/actualizarse desde otro hilo que no sea el hilo de interfaz de usuario (principal)

Eso no va a funcionar.

leí en alguna parte que no se puede modificar el subyacente esto desde otro hilo - esto parecería límite de un patrón MVC, ya que con este lista en particular, quiero añadir elementos de diferentes hilos

MVC no tiene nada que ver con los hilos.

puede ser seguro para actualizar la lista de subyacente de hilos de otros que la interfaz de usuario?

No. Otras hilos pueden desencadenar cambios al adaptador (por ejemplo, a través de post()), pero los propios actualizaciones deben ser procesados ​​en el hilo principal de la aplicación, para un adaptador que actualmente está unido a un ListView.

Además, si quiero modificar los datos dentro de un adaptador, puedo modificar la Lista subyacente o el adaptador sí mismo (a través de su add(), etc. Los métodos). La modificación de los datos a través del adaptador parece incorrecta.

modificar su Adapter a través de la misma para AdapterArrayAdapter. Modifique su Adapter a través del proveedor de base de datos/contenido subyacente para CursorAdapter. Otros adaptadores pueden variar.

Tengo un objeto llamado Queue que contiene una LinkedList. Queue extends Observable, y cuando las cosas se agregan a su lista interna a través de sus métodos , llamo a setChanged() y notifyListeners().

Ha considerado el uso LinkedBlockingQueue, en lugar de aplicar su propio hilo de seguridad Queue?

esta actividad, en su método onCreate(), registra un oyente de Observador a mi objeto Queue. En el método update() del Observer llamo al notifyDataSetChanged() en el Adaptador.

Adapters deben llamar notifyDataSetChanged() en sí mismos (si el cambio se realiza por ellos) o lo han llamado en ellos por la entidad que está cambiando los datos (por ejemplo, un Cursor para un CursorAdapter). Eso es MVC. El Activity no debe saber ni importar cuando cambia su modelo de datos.

Así que es como si el adaptador notado el cambio de la Lista antes del Observador tenía la oportunidad de notificar a sus observadores, y llamar a mi método para notificar al adaptador que el contenido había cambiado.

Posiblemente esté utilizando un ArrayAdapter, en cuyo caso todo este extra observador/notifica cosas se interpone en su camino, ya que eso se manejó para usted. Solo necesita organizar actualizar el ArrayAdapter en el hilo principal de la aplicación.

así que supongo que lo que estoy preguntando es, ¿ esta una buena manera de manipular en marcha un adaptador?

No particularmente, en mi humilde opinión.

¿Es un problema porque estoy actualizando contenidos del adaptador de un hilo que no sea el hilo de interfaz de usuario?

Si no está forzando las actualizaciones a la secuencia principal de la aplicación, esto finalmente se bloqueará, una vez que solucione sus otros problemas.

dan la cola de objeto a Handler al hilo interfaz de usuario cuando se crea, y crea todas las modificaciones en la Lista por que Handler, pero esto parece inadecuada

Se puede usar un Handler, o podría llamar al post() en su adjunto ListView.

apagado el brazalete, que crearía una subclase de ArrayAdapter llamado ThreadSafeArrayAdapter y lo uso en lugar de Queue. ThreadSafeArrayAdapter reemplaza add(), insert(), y remove() con los que tienen la superclase haciendo su cosa en el subproceso principal de la aplicación, a través de Handler o post().

+0

Gracias por sus comentarios, pero todavía estoy confundido en algunas cosas, si no le importa ayudar más. El nombre del objeto "Queue" es un poco inapropiado: es más una cola de medios de reproducción de videos o canciones para reproducir. Es una lista de elementos con un currentIndex int. Entendí que el Adaptador era solo un puente entre mi Modelo (mi Lista) y la vista (ListView). ¿Qué pasa si necesito actualizar los datos que muestra la vista desde otra actividad donde no tengo acceso al adaptador? Parecería natural actualizar la Lista subyacente, quizás desde un Runnable si la tarea que realiza este add pudiera bloquear. – skyler

+0

"Entendí que el Adaptador era solo un puente entre mi Modelo (mi Lista) y la vista (ListView)." Eso es generalmente cierto. "¿Qué ocurre si necesito actualizar los datos que muestra la vista desde otra Actividad en la que no tengo acceso al Adaptador?" Lo más probable es que tampoco tengas acceso a la Lista, entonces necesitarás una estrategia más elaborada. "Parecería natural actualizar la Lista subyacente, tal vez desde un Runnable si la tarea que realiza este add pudiera bloquear". En mi humilde opinión, el problema de la rosca es un problema de IU, por lo que recomiendo que su adaptador sea el que se encargue de la seguridad de las roscas. – CommonsWare

+0

Pero, puedes implementarlo como quieras, siempre y cuando solo actualices el Adaptador en el hilo principal de la aplicación. – CommonsWare

0

buen consejo general es http://developer.android.com/resources/articles/painless-threading.html

Personalmente utilizo mi hilo a medida (A que se extiende la clase Thread), sino enviar la respuesta al hilo de interfaz de usuario a través de un mensaje. Así, en función de la rosca run() no es:

Message msg; 
msg = Message.obtain(); 
msg.what = MSG_IMG_SET;      
mExtHandler.sendMessage(msg); 

El mExtHandler fue asignado a la instancia de controlador externo en el constructor hilo. El hilo de interfaz de usuario define el controlador de mensajes:

private Handler mImagesProgressHandler; 

public void onCreate(Bundle bundle) { 

    mImagesProgressHandler = new Handler() { 
    @Override 
    public void handleMessage(Message msg) { 
     switch (msg.what) {    
     case LoadImagesThread.MSG_IMG_SET: 
      mArrayAdapter.setBitmapList(mImagesList); 
      mArrayAdapter.notifyDataSetChanged(); 
      break; 
     case LoadImagesThread.MSG_ERROR: 
      break; 
     } 
     super.handleMessage(msg); 
    } 
};     

Esto es en realidad más fácil que AsyncTask.

+0

Tenga cuidado con las clases de controlador interno no estático. Puede ser un riesgo de pérdida de memoria. – HAxxor

+0

Buen punto, gracias. – Yar

Cuestiones relacionadas