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

189

Lo que quiero hacer : ejecutar un subproceso en segundo plano que calcule el contenido de ListView y actualice ListView parcialmente, mientras se calculan los resultados.

Lo que sé que debo evitar : no puedo meterme con el contenido de ListAdapter del subproceso en segundo plano, así que heredé AsyncTask y publiqué el resultado (agregue entradas al adaptador) de onProgressUpdate. Mi adaptador usa ArrayList de objetos de resultado, todas las operaciones en esas listas de matriz están sincronizadas.

La investigación de otras personas : no hay datos muy valiosos aquí . También sufrí bloqueos casi diarios para un grupo de ~ 500 usuarios, y cuando agregué el list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)bloqueo en onProgressUpdate, los bloqueos disminuyeron en un factor de 10 pero no desaparecieron. (se sugirió en respuesta )

Lo que recibí a veces : tenga en cuenta que ocurre muy raramente (una vez a la semana para uno de los 3.5k usuarios). Pero me gustaría deshacerme de este error por completo. Aquí hay un seguimiento parcial de la pila:

`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 abajo

RESPUESTA FINAL: Resultó que estaba llamando notifyDataSetChangedcada 5 inserciones para evitar parpadeos y cambios repentinos en la lista. No se puede hacer de esa manera, siempre notifique al adaptador cuando cambie la lista base. Este error ya hace mucho que me desapareció.

tomash
fuente
3
¿Llamaste a notifyDataSetChanged ()?
Denis Palnitsky
1
Por supuesto, en onProgressUpdate va la secuencia: list.setVisibility (GONE) - addObjectToList / operación sincronizada en la lista / - notifyDataSetChanged () - list.setVisibility (VISIBLE) (y sin modificaciones de visibilidad, la excepción ocurre con mucha más frecuencia)
tomash
1
¿Está modificando la ArrayList subyacente en un hilo diferente? Todos los cambios, incluso la ArrayList a la que hace referencia el Adaptador, tienen que ocurrir en el hilo de la interfaz de usuario.
Rich Schuler
2
@Qberticus: como dije claramente, no modifico ArrayList desde un hilo diferente, sino desde el método onProgressUpdate de AcyncTask, funciona en el hilo GUI.
tomash
1
@tomash obtuve la misma excepción que estoy usando pull para actualizar y en la ejecución posterior he escrito adapter.notifyDataSetChanged (); lvAutherlist.completeRefreshing (); pero a veces tengo este error cómo resolverlo
Khan

Respuestas:

119

Tuve el mismo problema.

Estaba agregando elementos a mi ArrayListhilo externo de la interfaz de usuario.

Solución: hice ambas cosas adding the itemsy llamé notifyDataSetChanged()al hilo de la interfaz de usuario.

Mullins
fuente
44
Agregué los elementos y llamé a notifyDataSetChanged () en el hilo de la interfaz de usuario y resolví esto.
Mullins
23
Comentario amable, de verdad.
dentex
2
Este es un problema de subprocesos múltiples y el uso de bloques sincronizados correctamente Esto se puede evitar. Sin poner cosas adicionales en UI Thread y causar pérdida de capacidad de respuesta de la aplicación. Verifique mi respuesta a continuación.
Javanator
2
Para futuros lectores: hacer cálculos en el subproceso en segundo plano es algo bueno, pero debe pasar los resultados al subproceso de la interfaz de usuario y agregar los elementos a la colección base del adaptador y notificar al adaptador en el mismo bloque de código.
tomash
44
@Mullins ¿Puede mostrar una muestra de su solución? Estoy atrapado en el mismo problema
Jas
27

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

requestLayout();

de la clase ListView

gian1200
fuente
35
¿Cuándo deberíamos llamar a este método?
JehandadK
3
@DeBuGGeR después de agregar elementos a ListView o cambiar su adaptador.
Ahmet Noyan Kızıltan
¿Qué pasa si estoy agregando elementos en asinctask?
Dr. aNdRO
2
Solo para el registro, @ Dr.aNdRO, usted recopila sus resultados en AsyncTask. doInBackground () y guarde sus resultados para actualizar la lista en AsyncTask.onPostExecute (), que se ejecutará en el hilo de la interfaz de usuario. Si necesita actualizar a medida que avanza, use AsyncTask.publishProgress () y AsyncTask.onProgressUpdate (), que también se ejecuta en el hilo de la interfaz de usuario.
Nicole Borrelli
21

Este es un problema de subprocesos múltiples y el uso de bloques sincronizados correctamente Esto se puede evitar. Sin poner cosas adicionales en UI Thread y causar pérdida de capacidad de respuesta de la aplicación.

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

Como puede ver para un caso normal. La actualización del adaptador de datos desde el subproceso en segundo plano y la llamada a notifyDataSetChanged en el subproceso de IU funciona.

Esta excepción de estado ilegal surge cuando un subproceso de interfaz de usuario está actualizando la vista y otro subproceso de fondo cambia los datos nuevamente. Ese momento causa este problema.

Entonces, si sincronizará todo el código que está cambiando los datos del adaptador y haciendo una llamada a notifydatasetchange. Este problema debería desaparecer. Como se fue para mí y todavía estoy actualizando los datos del hilo de fondo.

Aquí está el código específico de mi caso para que otros lo consulten.

Mi cargador en la pantalla principal carga los contactos de la guía telefónica 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 completa en los hashmaps. Que es directamente utilizable para los adaptadores de lista para dibujar la lista.

Hay un botón en mi pantalla. Eso abre una actividad donde se enumeran estos números de teléfono. Si configuro directamente Adaptador sobre la lista antes de que el hilo anterior termine su trabajo, el caso de navegación rápida ocurre con menos frecuencia. Aparece la excepción. Cuál es el título de esta pregunta SO. Entonces tengo que hacer algo como esto en la segunda actividad.

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

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

Eso resolvió mi problema.

Este código es solo un fragmento. Debe cambiarlo para compilarlo bien.

@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

Javanator
fuente
Lo siento, ¿cómo hiciste eso? ¿Cómo sincronizó el código de subproceso de fondo y la llamada de notificación de cambio de datos? Estoy usando un doInBackground al filtrar datos en un AutocompleteTextView y estoy enfrentando el mismo problema ... ¿Podría aconsejarme algo para resolver?
tonix
@ user3019105 - Sincronizando dos hilos de fondo. uno que actualiza los datos en segundo plano y otro que notifica al subproceso ui para establecerAdadpter o notificar el cambio de datos utilizando los métodos handler.post o los métodos runOnUiThread disponibles. Para mi caso particular, sincronicé dos cargadores con su doInBackground sincronizado en un objeto singleton. Uno comienza a preparar los datos en la pantalla de inicio para estar rápidamente disponibles para la pantalla del tablero. Esa era mi necesidad.
Javanator
¿Podría publicar un fragmento de código de esta implementación? En mi caso, resolví llamar a clear () en el adaptador, creando una ArrayList <T> temporal de objetos filtrados, luego realicé una llamada a addAll (tmpArrayList) y finalmente a notifyDataSetChanged (). Realizo todas estas llamadas dentro del método PubliqueResult () Filtro, ejecutándose en el hilo principal. ¿Crees que esta es una solución confiable?
tonix
@ user3019105 verifica la respuesta editada. Espero que sea útil para usted ahora
Javanator
esta respuesta requiere mucha lectura sobre por qué funciona, tutorials.jenkov.com/java-concurrency/synchronized.html es un buen lugar para comenzar
abdu
15

Resolví esto por tener 2 listas. Una lista que uso solo para el adaptador, y hago todos los cambios / actualizaciones de datos en la otra lista. Esto me permite hacer actualizaciones en una lista en un subproceso en segundo plano 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();
        }
    });
}
tríada
fuente
7

Escribí este código y lo ejecuté en una imagen del emulador 2.1 durante ~ 12 horas y no obtuve la IllegalStateException. Voy a darle al marco de Android el beneficio de la duda sobre este y diré que es muy probable que sea un error en su código. Espero que esto ayude. Tal vez pueda 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);
    }
}
Rich Schuler
fuente
¡Gracias por tu trabajo! Noté que para el método ArrayAdapter hasStableIds () = false; mi implementación devolvió true, lo que no era del todo correcto, ya que las filas se ordenaron antes de cada notifyDataSetChanged (). Lo intentaré; si los accidentes sobreviven, continuaré la investigación. ¡Atentamente!
tomash
4

Hace varios días me encontré con el mismo problema y provoca varios miles de bloqueos por día, aproximadamente el 0.1% de los usuarios se encuentran con esta situación. Lo intenté setVisibility(GONE/VISIBLE)y requestLayout(), pero el conteo de accidentes solo disminuye un poco.

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

Finalmente descubrí que la razón es que usé una Handlerllamada notifyDataSetChanged()después de actualizar los datos, lo que puede conducir a una especie de:

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

Y cometí otro error que getCount(), getItem()y getView(), uso directamente los campos en DataSource, en lugar de copiarlos en el adaptador. Entonces finalmente se bloquea cuando:

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

Entonces, la solución es fácil, solo copie datos al adaptador desde mi DataSource cuando mi controlador activa el adaptador para obtener datos y llamadas notifyDataSetChanged(). El choque ahora nunca vuelve a suceder.

HJWAJ
fuente
1
Este fue mi problema. En mi caso, getCount () estaba devolviendo el nuevo recuento disponible antes de que se llame a notifyDataSetChanged. Como mi lista era 'virtual', ahora capturo el recuento actualizado en mi método notifyDataSetChanged y luego devuelvo este recuento en caché cuando se me solicita el recuento a través de getCount ().
Glenn
3

Si esto sucediera de forma intermitente, resulta que solo tuve este problema cuando la lista se desplazó después de hacer clic en un último elemento de "cargar más". Si la lista no se desplazaba, todo funcionaba bien.

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

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 ocurre onChange, activa este código en AdapterView (padre de ListView)

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

¡Observe la forma en que NO se garantiza que el adaptador sea el mismo!

En mi caso, dado que era un 'LoadMoreAdapter', estaba devolviendo el WrappedAdapter en la llamada getAdapter (para acceder a los objetos subyacentes). Esto dio como resultado que los recuentos fueran diferentes debido al elemento adicional 'Cargar más' y a que se lanzara la Excepción.

Solo hice esto porque los documentos hacen que parezca 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 puede ser un WrapperListAdapter.

aaronvargas
fuente
Tengo un problema similar . ¿Puedes sugerir cómo solucionarlo?
May13ank
Si observa los 2 ejemplos de código anteriores, verá mAdapter en ListView y getAdapter () en un elemento primario de ListView (AdapterView). Si anula getAdapter (), el resultado de getAdapter () puede no ser el mAdapter. Si los recuentos de los 2 adaptadores son diferentes, obtendrá el error.
aaronvargas
3

Mi problema estaba relacionado con el uso de un filtro junto con 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);
}

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

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

La solución real es fácil, solo llame notifyDataSetChanged()antes de solicitar que se realice el filtrado:

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

Tengo una lista de objetos de alimentación. Se adjunta y se trunca desde el subproceso sin interfaz de usuario. Funciona bien con el adaptador a continuación. Llamo FeedAdapter.notifyDataSetChangedal hilo de la interfaz de usuario de todos modos, pero un poco más tarde. Me gusta esto porque mis objetos Feed permanecen en la memoria en el Servicio local incluso cuando la IU 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;
    }
}
ilya
fuente
2

Estaba 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 receptiva, configuro mAdapter.setNotifyOnChange(false)y llamo mAdapter.notifyDataSetChanged()4 veces por segundo. Una vez por segundo, se ordena la matriz.

Esto funciona bien y se ve muy adictivo, pero desafortunadamente es posible bloquearlo tocando los elementos de la lista que se muestran con la frecuencia suficiente.

Pero parece que he encontrado una solución aceptable. Supongo que incluso si solo trabajas en el subproceso de interfaz de usuario, el adaptador no acepta muchos cambios en sus datos sin llamar notifyDataSetChanged(), debido a esto creé una cola que almacena todos los elementos nuevos hasta que terminen los 300 ms mencionados. Si se llega a este momento, agrego todos los elementos almacenados de una sola vez y llamo notifyDataSetChanged(). Hasta ahora ya no podía bloquear la lista .

Lars K.
fuente
2

Incluso cuando enfrenté el mismo problema en mi aplicación de notificación XMPP, el mensaje de los receptores debe agregarse nuevamente a la vista de lista (implementado con ArrayList). Cuando intenté agregar el contenido del receptor a través de MessageListener(hilo separado), la aplicación se cierra con el error anterior. Lo resolví agregando el contenido a mi método arraylist& setListviewadapaterthrough runOnUiThread, que es parte de la clase Activity. Esto resolvió mi problema.

Balaji
fuente
1

Me enfrenté a un problema similar, así es como lo resolví en mi caso. Verifico si taskya está RUNNINGo FINISHEDporque una tarea puede ejecutarse solo 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);
    }

}

}
Leonardo Costa
fuente
1

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

Gusthema
fuente
1

Una de las causas de este bloqueo es que el ArrayListobjeto no puede cambiar por completo. Entonces, cuando elimino un elemento, tengo que hacer esto:

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

Esto solucionó el bloqueo para mí.

andude
fuente
1

En mi caso, llamé al método GetFilter()en un adaptador desde el TextWatcher()método de la actividad principal, y agregué los datos con un bucle For activado GetFilter(). La solución fue cambiar el bucle For por AfterTextChanged()el método secundario en la actividad principal y eliminar la llamada aGetFilter()

sek13300
fuente
1
Por favor aclare su solución. También estoy en una situación parecida a
ti
1
adapter.notifyDataSetChanged()
nada de joseph
fuente
2
Es una buena práctica en Stack Overflow agregar una explicación de por qué su solución debería funcionar o es mejor que las soluciones existentes. Para obtener más información, lea Cómo responder .
Samuel Liew
0

También recibía exactamente el mismo error y usaba 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 resolví poniendoadapter.notifyDataSetChanged(); en la parte inferior de mi hilo de interfaz de usuario, ese 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.

EDITAR: De hecho, mi aplicación todavía se bloqueaba cada 1 de cada 10 veces, dando el mismo error.

Finalmente me encontré runOnUiThreadcon una publicación 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 eliminé el adapter.notifyDataSetChanged();método. Ahora, mi aplicación nunca se bloquea.

CHarris
fuente
0

Pruebe una de estas soluciones:

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

  2. Asegúrese de que todas las actualizaciones de la IU se invoquen en el subproceso de la IU.

Phuc Tran
fuente
0

Tuve el mismo problema al agregar nuevos datos en el cargador de imágenes perezoso que acabo de poner

         adapter.notifyDataSetChanged();

en

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

espero que te ayude

Hobii Sgonf
fuente
0

Como @Mullins dijo "Agregué
los elementos y llamé notifyDataSetChanged()al hilo de la interfaz de usuario y resolví esto. - Mullins".

En mi caso tengo asynctasky llamé notifyDataSetChanged()al doInBackground()método y el problema está resuelto, cuando llamé desde onPostExecute()recibí la excepción.

Mina Ciro
fuente
0

Tenía una costumbre ListAdaptery estaba llamando super.notifyDataSetChanged()al principio y no al final del método

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

Tenía la misma situación, tenía muchos grupos de buttong dentro de mi elemento en la vista de lista y estaba cambiando algunos valores booleanos dentro de mi elemento como holder.rbVar.setOnclik ...

mi problema ocurrió porque estaba llamando a un método dentro de getView (); y estaba guardando un objeto dentro de sharepreference, así que tuve el mismo error arriba

Cómo lo resolví; Eliminé mi método dentro de getView () para notifyDataSetInvalidated () y el problema desapareció

   @Override
    public void notifyDataSetChanged() {
        saveCurrentTalebeOnShare(currentTalebe);
        super.notifyDataSetChanged();
    }
Sam
fuente
0

Yo tuve el mismo problema. finalmente obtuve la solución

antes de actualizar la vista de lista, si el teclado virtual está presente, ciérrelo primero. después de eso establezca la fuente de datos y llame a notifydatasetchanged ()

mientras se cierra el teclado internamente listview actualizará su interfaz de usuario. sigue llamando hasta cerrar el teclado. esa vez, si el origen de datos cambia, arrojará esta excepción. Si los datos se actualizan en onActivityResult, existe la posibilidad de que ocurra el mismo error.

 InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(v.getWindowToken(), 0);

        view.postDelayed(new Runnable() {
            @Override
            public void run() {
                refreshList();
            }
        },100L);
sreejith
fuente
0

Mi solución:

1) crear un temp ArrayList .

2) realiza tus trabajos pesados ​​(búsqueda de fila sqlite, ...) en doInBackground método y agregue elementos a la lista temporal de matrices.

3) agregue todos los elementos de temp araylist a su lista de arraylist en el onPostExecutemétodo.

note:es posible que desee eliminar algunos elementos de la vista de lista y también eliminar de la base de datos sqlite y tal vez eliminar algunos archivos relacionados con los elementos de sdcard, simplemente elimine los elementos de la base de datos y elimine sus archivos relacionados y agréguelos a la lista temporal de matrices background thread. entonces enUI thread elimine los elementos existentes en la lista temporal de la lista de la lista.

Espero que esto ayude.

Nadie8
fuente