Android, ListView IllegalStateException: "El contenido del adaptador ha cambiado pero ListView no recibió una notificación"

Qué quiero hacer : ejecutar un subproceso de fondo que calcula el contenido ListView y actualizar ListView parcialmente, mientras se calculan los resultados.

Lo que sé que tengo que evitar : No puedo meterse con el contenido ListAdapter de hilo de fondo, por lo que heredó AsyncTask y publicar resultado (añadir entradas al adaptador) de onProgressUpdate. Mi adaptador utiliza ArrayList de objetos de resultado, todas las operaciones en esos arraylists se sincronizan.

Investigación de otras personas : aquí hay datos muy valiosos. También sufrí accidentes casi diarios para un grupo de ~ 500 usuarios, y cuando agregué list.setVisibility(GONE)/trackList.setVisibility(VISIBLE) block en onProgressUpdate, los fallos bajaron por un factor de 10 pero no desaparecieron. (Se sugirió en respuesta )

Lo que tengo a veces : por favor note, realmente sucede raramente (una vez a la semana para uno de 3.5k usuarios). Pero me gustaría deshacerme de este error por completo. Aquí hay stacktrace parcial:

 `java.lang.IllegalStateException:` The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131296334, class android.widget.ListView) with Adapter(class com.transportoid.Tracks.TrackListAdapter)] at android.widget.ListView.layoutChildren(ListView.java:1432) at android.widget.AbsListView.onTouchEvent(AbsListView.java:2062) at android.widget.ListView.onTouchEvent(ListView.java:3234) at android.view.View.dispatchTouchEvent(View.java:3709) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:852) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884) [...] 

¿Ayuda? Ya no es necesario, ver más abajo

RESPUESTA FINAL: Como resultó, yo estaba llamando a notifyDataSetChanged cada 5 inserciones para evitar parpadeos y repentinos cambios en la lista. No se puede hacer de esa manera, siempre notificar el adaptador cuando la lista de base cambia. Este insecto que largo ido para mí ahora.

22 Solutions collect form web for “Android, ListView IllegalStateException: "El contenido del adaptador ha cambiado pero ListView no recibió una notificación"”

Tuve el mismo problema.

Estaba agregando elementos a mi ArrayList fuera del hilo de la interfaz de usuario.

Solución: He hecho ambos, adding the items y llamados notifyDataSetChanged() en el subproceso de la interfaz de usuario.

Tuve el mismo problema, pero lo arreglé usando el método

 requestLayout(); 

De la clase ListView

Esto es un problema MultiThreading y usar bloques correctamente sincronizados Esto se puede evitar. Sin poner cosas adicionales en el hilo de la interfaz de usuario y causar la pérdida de la capacidad de respuesta de la aplicación.

También me enfrenté a la misma. Y como la respuesta más aceptada sugiere hacer cambios en los datos del adaptador de la interfaz de usuario, Thread puede resolver el problema. Eso funcionará pero es una solución rápida y fácil pero no la mejor.

Como se puede ver en un caso normal. Actualización de adaptador de datos de hilo de fondo y llamada notifyDataSetChanged en hilos de interfaz de usuario funciona.

Esta excepción illegalStateException surge cuando un subproceso ui está actualizando la vista y otro subproceso de fondo cambia los datos de nuevo. Ese momento causa este problema.

Así que si va a sincronizar todo el código que está cambiando los datos del adaptador y hacer notificydatasetchange llamada. Este problema debería desaparecer. Como ido para mí y todavía estoy actualizando los datos de hilo de fondo.

Aquí está mi código específico de caso para que otros se refieran.

Mi cargador en la pantalla principal carga los contactos de la libreta de teléfonos en mis fuentes de datos en segundo plano.

  @Override public Void loadInBackground() { Log.v(TAG, "Init loadings contacts"); synchronized (SingleTonProvider.getInstance()) { PhoneBookManager.preparePhoneBookContacts(getContext()); } } 

Este PhoneBookManager.getPhoneBookContacts lee el contacto de la agenda telefónica y los llena en los hashmaps. Que es directamente utilizable para lista de adaptadores para dibujar la lista.

Hay un botón en mi pantalla. Esto abre una actividad en la que se enumeran estos números de teléfono. Si directamente setAdapter sobre la lista antes de que el hilo anterior termine su trabajo que es el caso naviagtion rápido ocurre menos a menudo. Aparece la excepción. Cuál es el título de esta pregunta SO. Así que tengo que hacer algo como esto en la segunda actividad.

Mi cargador en la segunda actividad espera que se complete el primer hilo. Hasta que muestre una barra de progreso. Compruebe el loadInBackground de ambos cargadores.

Entonces crea el adaptador y lo entrega a la actividad donde en el hilo del ui llamo a setAdapter.

Eso resolvió mi problema.

Este código es sólo un fragmento. Usted necesita cambiarlo para compilar bien para usted.

 @Override public Loader<PhoneBookContactAdapter> onCreateLoader(int arg0, Bundle arg1) { return new PhoneBookContactLoader(this); } @Override public void onLoadFinished(Loader<PhoneBookContactAdapter> arg0, PhoneBookContactAdapter arg1) { contactList.setAdapter(adapter = arg1); } /* * AsyncLoader to load phonebook and notify the list once done. */ private static class PhoneBookContactLoader extends AsyncTaskLoader<PhoneBookContactAdapter> { private PhoneBookContactAdapter adapter; public PhoneBookContactLoader(Context context) { super(context); } @Override public PhoneBookContactAdapter loadInBackground() { synchronized (SingleTonProvider.getInstance()) { return adapter = new PhoneBookContactAdapter(getContext()); } } } 

Espero que esto ayude

He resuelto esto por tener 2 listas. Una lista que uso sólo para el adaptador, y hago todos los cambios de datos / actualizaciones en la otra lista. Esto me permite hacer actualizaciones en una lista en un hilo de fondo, y luego actualizar la lista "adaptador" en el subproceso principal / UI:

 List<> data = new ArrayList<>(); List<> adapterData = new ArrayList(); ... adapter = new Adapter(adapterData); listView.setAdapter(adapter); // Whenever data needs to be updated, it can be done in a separate thread void updateDataAsync() { new Thread(new Runnable() { @Override public void run() { // Make updates the "data" list. ... // Update your adapter. refreshList(); } }).start(); } void refreshList() { runOnUiThread(new Runnable() { @Override public void run() { adapterData.clear(); adapterData.addAll(data); adapter.notifyDataSetChanged(); listView.invalidateViews(); } }); } 

Escribí este código y lo tenía funcionado en una imagen del emulador 2.1 para ~ 12 horas y no conseguí el IllegalStateException. Voy a dar el marco de Android el beneficio de la duda sobre este y decir que es más probable un error en su código. Espero que esto ayude. Tal vez usted puede adaptarlo a su lista y datos.

 public class ListViewStressTest extends ListActivity { ArrayAdapter<String> adapter; ListView list; AsyncTask<Void, String, Void> task; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1); this.list = this.getListView(); this.list.setAdapter(this.adapter); this.task = new AsyncTask<Void, String, Void>() { Random r = new Random(); int[] delete; volatile boolean scroll = false; @Override protected void onProgressUpdate(String... values) { if(scroll) { scroll = false; doScroll(); return; } if(values == null) { doDelete(); return; } doUpdate(values); if(ListViewStressTest.this.adapter.getCount() > 5000) { ListViewStressTest.this.adapter.clear(); } } private void doScroll() { if(ListViewStressTest.this.adapter.getCount() == 0) { return; } int n = r.nextInt(ListViewStressTest.this.adapter.getCount()); ListViewStressTest.this.list.setSelection(n); } private void doDelete() { int[] d; synchronized(this) { d = this.delete; } if(d == null) { return; } for(int i = 0 ; i < d.length ; i++) { int index = d[i]; if(index >= 0 && index < ListViewStressTest.this.adapter.getCount()) { ListViewStressTest.this.adapter.remove(ListViewStressTest.this.adapter.getItem(index)); } } } private void doUpdate(String... values) { for(int i = 0 ; i < values.length ; i++) { ListViewStressTest.this.adapter.add(values[i]); } } private void updateList() { int number = r.nextInt(30) + 1; String[] strings = new String[number]; for(int i = 0 ; i < number ; i++) { strings[i] = Long.toString(r.nextLong()); } this.publishProgress(strings); } private void deleteFromList() { int number = r.nextInt(20) + 1; int[] toDelete = new int[number]; for(int i = 0 ; i < number ; i++) { int num = ListViewStressTest.this.adapter.getCount(); if(num < 2) { break; } toDelete[i] = r.nextInt(num); } synchronized(this) { this.delete = toDelete; } this.publishProgress(null); } private void scrollSomewhere() { this.scroll = true; this.publishProgress(null); } @Override protected Void doInBackground(Void... params) { while(true) { int what = r.nextInt(3); switch(what) { case 0: updateList(); break; case 1: deleteFromList(); break; case 2: scrollSomewhere(); break; } try { Thread.sleep(0); } catch(InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; this.task.execute(null); } } 

Mi problema estaba relacionado con el uso de un filtro junto con el ListView.

Al configurar o actualizar el modelo de datos subyacente de ListView, estaba haciendo algo como esto:

 public void updateUnderlyingContacts(List<Contact> newContacts, String filter) { this.allContacts = newContacts; this.filteredContacts = newContacts; getFilter().filter(filter); } 

El filter() llamada filter() en la última línea hará (y debe) hacer que notifyDataSetChanged() sea ​​llamado en el método publishResults() del Filtro. Esto puede funcionar bien a veces, especialmente en mi rápido Nexus 5. Pero en realidad, es ocultar un error que se notará con dispositivos más lentos o en condiciones de uso intensivo de recursos.

El problema es que el filtrado se realiza de forma asíncrona y, por lo tanto, entre el final de la instrucción filter() y la llamada a publishResults() , tanto en el subproceso UI, algún otro código de subproceso UI puede ejecutar y cambiar el contenido del adaptador.

La corrección real es fácil, solo llama a notifyDataSetChanged() también antes de solicitar el filtrado a realizar:

 public void updateUnderlyingContacts(List<Contact> newContacts, String filter) { this.allContacts = newContacts; this.filteredContacts = newContacts; notifyDataSetChanged(); // Fix getFilter().filter(filter); } 

Tengo una lista si los objetos de alimentación. Se agrega y se trunca desde ningún subproceso de UI. Funciona bien con el adaptador a continuación. Llamo FeedAdapter.notifyDataSetChanged en el hilo de interfaz de usuario de todos modos, pero poco más tarde. Me gusta esto porque mis objetos de Feed permanecen en la memoria en el servicio local incluso cuando la interfaz de usuario está muerta.

 public class FeedAdapter extends BaseAdapter { private int size = 0; private final List<Feed> objects; public FeedAdapter(Activity context, List<Feed> objects) { this.context = context; this.objects = objects; size = objects.size(); } public View getView(int position, View convertView, ViewGroup parent) { ... } @Override public void notifyDataSetChanged() { size = objects.size(); super.notifyDataSetChanged(); } @Override public int getCount() { return size; } @Override public Object getItem(int position) { try { return objects.get(position); } catch (Error e) { return Feed.emptyFeed; } } @Override public long getItemId(int position) { return position; } } 

Estoy enfrentando el mismo problema con exactamente el mismo registro de errores. En mi caso onProgress() de AsyncTask agrega los valores al adaptador usando mAdapter.add(newEntry) . Para evitar que la interfaz de usuario se vuelva menos sensible, establezco mAdapter.setNotifyOnChange(false) y llamo mAdapter.notifyDataSetChanged() 4 veces la segunda. Una vez por segundo se ordena la matriz.

Este trabajo bien y parece muy adictivo, pero por desgracia es posible que se bloquee tocando los elementos de la lista se muestra a menudo.

Pero parece que he encontrado una solución aceptable. Mi conjetura que incluso si usted apenas trabaja en el hilo del ui el adaptador no acepta muchos cambios a sus datos sin llamar a notifyDataSetChanged() , debido a esto he creado una cola que esté almacenando todos los nuevos artículos hasta que los 300ms mencionados están sobre. Si este momento se alcanza agrego todos los artículos almacenados en una toma y llamo a notifyDataSetChanged() . Hasta ahora no pude colapsar la lista .

Se trata de un error conocido en Android 4 a 4.4 (KitKat) y se resuelve en "> 4.4"

Consulta aquí: https://code.google.com/p/android/issues/detail?id=71936

Incluso me enfrenté al mismo problema en mi aplicación de notificación XMPP, el mensaje de los receptores debe ser añadido de nuevo a la vista de lista (implementado con ArrayList ). Cuando intenté agregar el contenido del receptor con MessageListener (el hilo de rosca separado), la aplicación para con el error antedicho. He resuelto esto añadiendo el contenido a mi arraylist & setListviewadapater través del método runOnUiThread que es parte de la clase Activity. Esto solucionó mi problema.

Hace varios días me encontré con el mismo problema y provoca varios miles de accidentes por día, alrededor del 0,1% de los usuarios cumplir con esta situación. Traté de setVisibility(GONE/VISIBLE) y requestLayout() , pero el recuento sólo disminuye un poco.

Y finalmente lo resolví. Nada con setVisibility(GONE/VISIBLE) . Nada con requestLayout() .

Finalmente encontré la razón es que utilicé un Handler para llamar a notifyDataSetChanged() después de los datos de actualización, lo que puede llevar a una especie de:

  1. Actualiza los datos a un objeto modelo (lo llamo DataSource)
  2. El usuario toca listview (que puede llamar a checkForTap() / onTouchEvent() y finalmente llama a layoutChildren() )
  3. El adaptador obtiene los datos del objeto modelo y llama a notifyDataSetChanged() y actualiza las vistas

Y cometí otro error que en getCount() , getItem() y getView() , directamente utilizo campos en DataSource, en lugar de copiarlos al adaptador. Así que finalmente se bloquea cuando:

  1. El adaptador actualiza los datos que la última respuesta da
  2. Cuando devuelve la siguiente respuesta, DataSource actualiza los datos, lo que provoca el cambio del recuento de elementos
  3. El usuario toca la vista de lista, que puede ser un toque o un movimiento o una vuelta
  4. getCount() y getView() se llama, y ​​listview encuentra que los datos no son consistentes, y lanza excepciones como java.lang.IllegalStateException: The content of the adapter has changed but... Otra excepción común es una excepción IndexOutOfBoundException si utiliza el encabezado / pie de página en ListView .

Así que la solución es fácil, sólo copiar datos al adaptador de mi DataSource cuando mi manejador dispara adaptador para obtener datos y llama a notifyDataSetChanged() . El choque ahora nunca vuelve a suceder.

Si esto hubiera ocurrido de forma intermitente, resulta que sólo tuve este problema cuando la lista se desplazó después de que se hiciera clic en un último elemento de "carga más". Si la lista no se desplazaba, todo funcionaba bien.

Después de MUCH depuración, fue un error de mi parte, pero una inconsistencia en el código de Android también.

Cuando ocurre la validación, este código se ejecuta en ListView

  } else if (mItemCount != mAdapter.getCount()) { throw new IllegalStateException("The content of the adapter has changed but " + "ListView did not receive a notification. Make sure the content of " 

Pero cuando onChange sucede se dispara este código en AdapterView (padre de ListView)

  @Override public void onChanged() { mDataChanged = true; mOldItemCount = mItemCount; mItemCount = getAdapter().getCount(); 

Observe la forma en que el adaptador no está garantizado para ser el mismo!

En mi caso, ya que era un 'LoadMoreAdapter' estaba devolviendo el WrappedAdapter en la llamada getAdapter (para el acceso a los objetos subyacentes). Esto resultó en que los recuentos eran diferentes debido al elemento extra "Cargar más" y la Excepción que se lanzó.

Sólo hice esto porque los documentos hacen parecer que está bien hacerlo

ListView.getAdapter javadoc

Devuelve el adaptador actualmente en uso en este ListView. El adaptador devuelto puede no ser el mismo adaptador pasado a setAdapter (ListAdapter) pero podría ser un WrapperListAdapter.

Tuve el mismo problema y lo resolví. Mi problema era que estaba utilizando un listview , con un adaptador de matriz y con filtro. En el método performFiltering estaba jugando con la matriz que tienen los datos y fue el problema ya que este método no se está ejecutando en el hilo de interfaz de usuario y EVENTUALMENTE plantea algunos problemas.

Una causa de este error es que el objeto ArrayList no puede cambiar por completo. Por lo tanto, cuando elimino un elemento, tengo que hacer esto:

 mList.clear(); mList.addAll(newDataList); 

Esto arregló el accidente para mí.

En mi caso llamé al método GetFilter() en un adaptador del método TextWatcher() en la actividad principal, y agregué los datos con un bucle For en GetFilter() . La solución fue cambiar el bucle For AfterTextChanged() sub método en Main Activity y eliminar la llamada a GetFilter()

También estaba recibiendo el mismo error y usando AsyncTask:

 `java.lang.IllegalStateException:` The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131296334, class android.widget.ListView) with Adapter... etc 

Lo solucioné poniendo adapter.notifyDataSetChanged(); En la parte inferior de mi hilo de interfaz de usuario, que es mi método asyncTask onPostExecute. Me gusta esto :

  protected void onPostExecute(Void aVoid) { all my other stuff etc... all my other stuff etc... adapter.notifyDataSetChanged(); } }); } 

Ahora mi aplicación funciona.

EDIT: De hecho, mi aplicación todavía se estrelló sobre cada 1 en 10 veces, dando el mismo error.

Eventualmente me encontré con runOnUiThread en un post anterior, que pensé que podría ser útil. Así que lo puse en mi método doInBackground, así:

 @Override protected Void doInBackground(Void... voids) { runOnUiThread(new Runnable() { public void run() { etc... etc... 

Y he eliminado el adapter.notifyDataSetChanged(); método. Ahora, mi aplicación nunca se bloquea.

Me enfrenté a un problema similar, aquí es cómo he resuelto en mi caso. Comprobar si la task ya está en ejecución o FINISHED porque una tarea puede ejecutarse sólo una vez. A continuación, verá un código parcial y adaptado de mi solución.

 public class MyActivity... { private MyTask task; @Override protected void onCreate(Bundle savedInstanceState) { // your code task = new MyTask(); setList(); } private void setList() { if (task != null) if (task.getStatus().equals(AsyncTask.Status.RUNNING)){ task.cancel(true); task = new MyTask(); task.execute(); } else if (task.getStatus().equals(AsyncTask.Status.FINISHED)) { task = new MyTask(); task.execute(); } else task.execute(); } class MyTask extends AsyncTask<Void, Item, Void>{ List<Item> Itens; @Override protected void onPreExecute() { //your code list.setVisibility(View.GONE); adapterItem= new MyListAdapter(MyActivity.this, R.layout.item, new ArrayList<Item>()); list.setAdapter(adapterItem); adapterItem.notifyDataSetChanged(); } @Override protected Void doInBackground(Void... params) { Itens = getItens(); for (Item item : Itens) { publishProgress(item ); } return null; } @Override protected void onProgressUpdate(Item ... item ) { adapterItem.add(item[0]); } @Override protected void onPostExecute(Void result) { //your code adapterItem.notifyDataSetChanged(); list.setVisibility(View.VISIBLE); } } } 

Prueba una de estas soluciones:

  1. A veces, si agrega un nuevo objeto a la lista de datos en un subproceso (o método doInBackground ), se producirá este error. La solución es: crear una lista temporal y agregar datos a esta lista en el hilo (o doInBackground ), luego copiar todos los datos de la lista temporal a la lista del adaptador en el subproceso de la interfaz de usuario (o onPostExcute )

  2. Asegúrese de que todas las actualizaciones de interfaz de usuario se llaman en el subproceso de interfaz de usuario.

Tengo el mismo problema cuando agrego nuevos datos en el cargador de imágenes perezoso que acaba de poner

  adapter.notifyDataSetChanged(); 

en

  protected void onPostExecute(Void args) { adapter.notifyDataSetChanged(); // Close the progressdialog mProgressDialog.dismiss(); } 

Espero que te ayude

Al igual que @Mullins dijo "
Tanto añadí los elementos y llamó notifyDataSetChanged() en el hilo de la interfaz de usuario y resolví esto. – Mullins ".

En mi caso tengo asynctask y llamé notifyDataSetChanged() en el método doInBackground() y el problema se resuelve, cuando llamé desde onPostExecute() recibí la excepción.

Tenía un ListAdapter personalizado y estaba llamando a super.notifyDataSetChanged() al principio y no al final del método

 @Override public void notifyDataSetChanged() { recalculate(); super.notifyDataSetChanged(); } 

Tuve la misma sittuation, tuve muchos buttongroup insite mi elemento en listview y estaba cambiando algunos valores booleanos dentro de mi elemento como holder.rbVar.setOnclik …

Mi problema se produjo porque estaba llamando a un método dentro de getView (); Y estaba guardando un objeto dentro de sharepreference, así que tuve el mismo error anterior

Cómo lo solucioné; Quité mi método dentro de getView () para notifyDataSetInvalidated () y el problema ido

  @Override public void notifyDataSetChanged() { saveCurrentTalebeOnShare(currentTalebe); super.notifyDataSetChanged(); } 
  • Problema al usar una fuente personalizada - "no se puede hacer el tipo de letra nativo"
  • Resaltar texto buscado en elementos ListView
  • Cambiar las teclas de función de fondo de negro a transparente en la aplicación android
  • Android: ListView con botones -> OnItemClick no hacer nada
  • ¿Cómo puedo configurar una fuente personalizada en mi listview?
  • ¿Cómo superponer el color del texto del elemento de la lista en una región específica?
  • El fondo Listview es gris en Droid 3
  • Setvisibility (view.visible) no funciona después de setvisibility (view.gone)
  • ¿Lista de múltiples opciones con vista personalizada?
  • Haga clic en el evento de la casilla de verificación en la vista de lista personalizada en Android
  • Convertir elementos de vista de lista en una sola imagen de mapa de bits
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.