Android, ListView IllegalStateException: "Il contenuto dell'adattatore è cambiato ma ListView non ha ricevuto una notifica"


189

Cosa voglio fare : eseguire un thread in background che calcola i contenuti di ListView e aggiorni parzialmente ListView, mentre i risultati vengono calcolati.

Quello che so devo evitare : non posso scherzare con i contenuti di ListAdapter dal thread in background, quindi ho ereditato AsyncTask e ho pubblicato i risultati (aggiungi voci all'adattatore) da onProgressUpdate. Il mio adattatore utilizza ArrayList di oggetti risultato, tutte le operazioni su tali array sono sincronizzate.

La ricerca di altre persone : non è molto prezioso dati qui . Ho anche sofferto di crash quasi giornalieri per un gruppo di ~ 500 utenti e quando ho aggiunto il list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)blocco su onProgressUpdate, gli arresti anomali si sono ridotti di un fattore 10 ma non sono scomparsi. (è stato suggerito in risposta )

Quello che ho ricevuto a volte : si prega di notare che succede molto raramente (una volta alla settimana per uno degli utenti da 3,5k). Ma vorrei liberarmi completamente di questo errore. Ecco stack stack parziale:

`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)
[...]

Aiuto? Non più necessario, vedi sotto

RISPOSTA FINALE: Come ho scoperto, stavo chiamando notifyDataSetChangedogni 5 inserzioni per evitare sfarfallio e improvvisi cambi di lista. Non può essere fatto in questo modo, avvisare sempre l'adattatore quando cambia l'elenco di base. Questo bug è ormai scomparso da tempo.


3
Hai chiamato notifyDataSetChanged ()?
Denis Palnitsky,

1
Ovviamente, in onProgressUpdate segue la sequenza: list.setVisibility (GONE) - addObjectToList / operazione sincronizzata sulla lista / - notificationDataSetChanged () - list.setVisibility (VISIBLE) (e senza modifiche di visibilità l'eccezione si verifica molto più spesso)
tomash

1
Stai modificando la ArrayList sottostante su un thread diverso? Tutte le modifiche, anche all'ArrayList a cui fa riferimento l'adapter, devono verificarsi sul thread dell'interfaccia utente.
Rich Schuler,

2
@Qberticus - come ho affermato chiaramente, non modifico ArrayList da thread diversi, ma dal metodo onProgressUpdate di AcyncTask - funziona nel thread GUI.
Tomash,

1
@tomash ho avuto la stessa eccezione che sto usando pull per aggiornare e in post esecuzione ho scritto adapter.notifyDataSetChanged (); lvAutherlist.completeRefreshing (); ma a volte viene visualizzato questo errore su come risolverlo
Khan,

Risposte:


119

Ho avuto lo stesso problema.

Stavo aggiungendo elementi al mio ArrayListthread esterno dell'interfaccia utente.

Soluzione: ho fatto entrambe le cose adding the itemse ho chiamato notifyDataSetChanged()il thread dell'interfaccia utente.


4
Entrambi ho aggiunto gli elementi e ho chiamato notifyDataSetChanged () nel thread dell'interfaccia utente e l'ho risolto.
Mullins,

23
Commento geniale, davvero.
Dentex,

2
Questo è un problema di multithreading e l'utilizzo di blocchi correttamente sincronizzati Ciò può essere prevenuto. Senza aggiungere altro al thread dell'interfaccia utente e causare la perdita di reattività dell'app. Controlla la mia risposta di seguito.
Javanator,

2
Per i lettori futuri: fare calcoli nel thread in background è una buona cosa, ma dovresti passare i risultati al thread dell'interfaccia utente e aggiungere gli elementi alla raccolta base dell'adattatore e notificare l'adattatore nello stesso blocco di codice.
Tomash,

4
@Mullins Puoi mostrare un campione per la tua soluzione? Sono bloccato allo stesso problema
Jas

27

Ho avuto lo stesso problema, ma l'ho risolto usando il metodo

requestLayout();

dalla classe ListView


35
quando dovremmo chiamare questo metodo?
JehandadK,

3
@DeBuGGeR dopo aver aggiunto elementi a ListView o aver cambiato l'adattatore.
Ahmet Noyan Kızıltan,

cosa succede se aggiungo elementi in asynctask
Dr. aNdRO

2
Solo per la cronaca, @ Dr.aNdRO, raccogli i risultati in AsyncTask. doInBackground () e salva i risultati per aggiornare l'elenco in AsyncTask.onPostExecute (), che verrà eseguito nel thread dell'interfaccia utente. Se è necessario aggiornare mentre si procede, utilizzare AsyncTask.publishProgress () e AsyncTask.onProgressUpdate (), che viene eseguito anche nel thread dell'interfaccia utente.
Nicole Borrelli,

21

Questo è un problema di MultiThreading e l'utilizzo di blocchi correttamente sincronizzati Ciò può essere prevenuto. Senza aggiungere ulteriori elementi al thread dell'interfaccia utente e causare la perdita di reattività dell'app.

Ho anche affrontato lo stesso. E poiché la risposta più accettata suggerisce che la modifica dei dati dell'adattatore da UI Thread può risolvere il problema. Funzionerà ma è una soluzione semplice e veloce ma non la migliore.

Come puoi vedere per un caso normale. L'aggiornamento dell'adattatore dati dal thread in background e la chiamata notificationDataSetChanged nel thread dell'interfaccia utente funzionano.

Questa illegaleStateException sorge quando un thread dell'interfaccia utente sta aggiornando la vista e un altro thread in background modifica nuovamente i dati. Quel momento causa questo problema.

Quindi, se si sincronizzerà tutto il codice che sta modificando i dati dell'adattatore e che sta effettuando una chiamata di scambio di notifica. Questo problema dovrebbe essere risolto. Per quanto mi riguarda, sto ancora aggiornando i dati dal thread in background.

Ecco il mio codice specifico del caso a cui gli altri possono fare riferimento.

Il mio caricatore nella schermata principale carica i contatti della rubrica nelle mie origini dati in background.

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

Questo PhoneBookManager.getPhoneBookContacts legge i contatti dalla rubrica e li riempie negli hashaps. Che è direttamente utilizzabile dagli elenchi di elenchi per disegnare l'elenco.

C'è un pulsante sul mio schermo. Ciò apre un'attività in cui sono elencati questi numeri di telefono. Se ho impostato direttamente Adapter sull'elenco prima che il thread precedente finisca il suo funzionamento, il che è un caso di naviagtion veloce che si verifica meno spesso. Si apre l'eccezione. Qual è il titolo di questa domanda SO. Quindi devo fare qualcosa del genere nella seconda attività.

Il mio caricatore nella seconda attività attende il completamento del primo thread. Finché non mostra una barra di avanzamento. Controllare il loadInBackground di entrambi i caricatori.

Quindi crea l'adattatore e lo consegna all'attività in cui sul mio thread chiamo setAdapter.

Ciò ha risolto il mio problema.

Questo codice è solo uno snippet. Devi modificarlo per compilare bene per te.

@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());    
        }
    }

}

Spero che questo ti aiuti


Scusa come hai fatto? Come hai sincronizzato il codice del thread in background e la chiamata notifydatasetchange? Sto usando un doInBackground durante il filtraggio dei dati su un AutocompleteTextView e sto affrontando lo stesso problema ... Potresti consigliarmi qualcosa per risolvere?
tonix,

@ user3019105 - Sincronizzazione di due thread in background. uno che aggiorna i dati in background e l'altro che notifica al thread dell'interfaccia utente di impostareAdadpter o notifydatasetchanged utilizzando i metodi handler.post o runOnUiThread disponibili. Per il mio caso particolare ho sincronizzato due caricatori con il loro doInBackground sincronizzato su un oggetto singleton. Un inizio prepara i dati sulla schermata iniziale per renderli rapidamente disponibili per la schermata del pannello. Questo era il mio bisogno.
Javanator

Potresti pubblicare un frammento di codice di questa implementazione? Nel mio caso, ho risolto la chiamata a clear () sull'adattatore, creando un ArrayList temporaneo <T> di oggetti filtrati, quindi facendo una chiamata a addAll (tmpArrayList) e, infine, a notificationDataSetChanged (). Faccio tutte queste chiamate all'interno del metodo di filtro publishingResult (), in esecuzione sul thread principale. Pensi che questa sia una soluzione affidabile?
tonix,

@ user3019105 controlla la risposta modificata. Spero che ti sia utile ora
Javanator

questa risposta richiede molte letture sul perché funziona, tutorials.jenkov.com/java-concurrency/synchronized.html è un buon punto di partenza
abdu

15

Ho risolto questo con 2 liste. Un elenco che uso solo per l'adattatore e faccio tutte le modifiche / gli aggiornamenti dei dati sull'altro elenco. Questo mi permette di fare aggiornamenti su un elenco in un thread in background e quindi aggiornare l'elenco "adattatore" nel thread principale / dell'interfaccia utente:

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();
        }
    });
}

7

Ho scritto questo codice e l'ho fatto funzionare in un'immagine dell'emulatore 2.1 per ~ 12 ore e non ho ricevuto IllegalStateException. Darò al framework Android il beneficio del dubbio su questo e dirò che è molto probabilmente un errore nel tuo codice. Spero che aiuti. Forse puoi adattarlo alla tua lista e ai tuoi dati.

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);
    }
}

Grazie per il tuo lavoro! Ho notato che per il metodo ArrayAdapter hasStableIds () = false; la mia implementazione ha restituito true che non era del tutto corretto, poiché le righe sono state ordinate prima di ogni notificationDataSetChanged (). Ci proverò: se gli incidenti sopravvivono, continuerò le indagini. I migliori saluti!
tomash,

4

Diversi giorni fa ho riscontrato lo stesso problema e causano diverse migliaia di arresti anomali al giorno, circa lo 0,1% degli utenti incontra questa situazione. Ho provato setVisibility(GONE/VISIBLE)e requestLayout(), ma il conteggio degli arresti anomali diminuisce solo leggermente.

E finalmente l'ho risolto. Niente con setVisibility(GONE/VISIBLE). Niente con requestLayout().

Alla fine ho scoperto il motivo per cui ho usato un Handlerper chiamare notifyDataSetChanged()dopo l'aggiornamento dei dati, il che può portare a una sorta di:

  1. Aggiorna i dati su un oggetto modello (lo chiamo DataSource)
  2. L'utente tocca la visualizzazione elenco (che può chiamare checkForTap()/ onTouchEvent()e infine chiama layoutChildren())
  3. L'adapter ottiene i dati dall'oggetto modello e chiama notifyDataSetChanged()e aggiorna le viste

E ho fatto un altro errore che getCount(), getItem()e getView(), utilizzo direttamente i campi in DataSource, piuttosto che copiarli nell'adattatore. Quindi alla fine si blocca quando:

  1. L'adattatore aggiorna i dati forniti dall'ultima risposta
  2. Alla successiva risposta, DataSource aggiorna i dati, causando la modifica del conteggio degli articoli
  3. L'utente tocca la visualizzazione elenco, che può essere un tocco o uno spostamento o una vibrazione
  4. getCount()e getView()viene chiamato e listview rileva che i dati non sono coerenti e genera eccezioni come java.lang.IllegalStateException: The content of the adapter has changed but.... Un'altra eccezione comune è IndexOutOfBoundExceptionse si usa intestazione / piè di pagina in ListView.

Quindi la soluzione è semplice, copio i dati sull'adattatore dal mio DataSource quando il mio gestore attiva l'adattatore per ottenere dati e chiamate notifyDataSetChanged(). L'incidente ora non si ripete più.


1
Questo era il mio problema. Nel mio caso getCount () stava restituendo il nuovo conteggio disponibile prima che fosse chiamato callDataSetChanged. Poiché il mio elenco era "virtuale", ora acquisisco il conteggio aggiornato nel mio metodo notifyDataSetChanged e quindi restituisco questo conteggio cache quando viene richiesto il conteggio tramite getCount ().
Glenn,

3

Se ciò fosse accaduto in modo intermittente, risulta che ho avuto questo problema solo quando l'elenco è stato fatto scorrere dopo che è stato fatto clic su un ultimo elemento "carica altro". Se l'elenco non è stato fatto scorrere, tutto ha funzionato bene.

Dopo il debug MOLTO, è stato un bug da parte mia, ma anche un'incoerenza nel codice Android.

Quando si verifica la convalida, questo codice viene eseguito in 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 "

Ma quando succede onChange genera questo codice in AdapterView (padre di ListView)

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

Notare come NON è garantito che l'adattatore sia uguale!

Nel mio caso, dato che era un "LoadMoreAdapter", restituivo WrappedAdapter nella chiamata getAdapter (per l'accesso agli oggetti sottostanti). Ciò ha comportato che i conteggi fossero diversi a causa dell'elemento aggiuntivo "Carica altro" e dell'eccezione generata.

L'ho fatto solo perché i documenti fanno sembrare che vada bene

ListView.getAdapter javadoc

Restituisce l'adattatore attualmente in uso in questo ListView. L'adattatore restituito potrebbe non essere lo stesso adattatore passato a setAdapter (ListAdapter) ma potrebbe essere un WrapperListAdapter.


Sto riscontrando un problema simile. Potete per favore suggerire come risolverlo.
13

Se guardi i 2 esempi di codice sopra, vedrai mAdapter in ListView e getAdapter () in un genitore di ListView (AdapterView). Se si sostituisce getAdapter (), il risultato di getAdapter () potrebbe non essere mAdapter. Se i conteggi dei 2 adattatori sono diversi, viene visualizzato l'errore.
aaronvargas,

3

Il mio problema era legato all'uso di un filtro insieme a ListView.

Durante l'impostazione o l'aggiornamento del modello di dati sottostante di ListView, stavo facendo qualcosa del genere:

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

La chiamata filter()nell'ultima riga comporterà (e dovrà) notifyDataSetChanged()essere chiamata nel publishResults()metodo del filtro . Questo a volte può funzionare bene, specialmente nel mio veloce Nexus 5. Ma in realtà, nasconde un bug che noterai con dispositivi più lenti o in condizioni ad alta intensità di risorse.

Il problema è che il filtro viene eseguito in modo asincrono, quindi tra la fine filter()dell'istruzione e la chiamata a publishResults(), sia nel thread dell'interfaccia utente, sia possibile eseguire qualche altro codice thread dell'interfaccia utente e modificare il contenuto dell'adattatore.

La correzione effettiva è semplice, basta chiamare notifyDataSetChanged()anche prima di richiedere l'esecuzione del filtro:

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

3

Ho un elenco di oggetti Feed. Viene aggiunto e troncato dal thread di nessuna interfaccia utente. Funziona bene con l'adattatore sottostante. Chiamo FeedAdapter.notifyDataSetChangedcomunque il thread dell'interfaccia utente, ma poco dopo. Mi piace perché i miei oggetti Feed rimangono in memoria nel servizio locale anche quando l'interfaccia utente è morta.

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;
    }
}

2

Ho riscontrato lo stesso problema con esattamente lo stesso registro errori. Nel mio caso onProgress()di AsyncTask aggiunge i valori all'adattatore usando mAdapter.add(newEntry). Per evitare che l'interfaccia utente diventi meno reattiva, imposto mAdapter.setNotifyOnChange(false)e chiamo mAdapter.notifyDataSetChanged()4 volte il secondo. Una volta al secondo l'array viene ordinato.

Funziona bene e sembra molto avvincente, ma sfortunatamente è possibile bloccarlo toccando le voci dell'elenco mostrate abbastanza spesso.

Ma sembra che ho trovato una soluzione accettabile. Suppongo che anche se lavori solo sul thread dell'interfaccia utente, l'adattatore non accetta molte modifiche ai suoi dati senza chiamare notifyDataSetChanged(), per questo motivo ho creato una coda che memorizza tutti i nuovi elementi fino a quando i 300ms menzionati non sono finiti. Se viene raggiunto questo momento, aggiungo tutti gli elementi memorizzati in un colpo solo e chiamo notifyDataSetChanged(). Fino ad ora non sono stato più in grado di bloccare l'elenco .



2

Anche se ho riscontrato lo stesso problema nella mia applicazione di notifica XMPP, il messaggio del destinatario deve essere aggiunto alla visualizzazione elenco (implementata con ArrayList). Quando ho provato ad aggiungere il contenuto del ricevitore tramite MessageListener(thread separato), l'applicazione si chiude con l'errore sopra riportato. Ho risolto questo aggiungendo il contenuto al mio metodo arraylist& setListviewadapaterthrough runOnUiThreadche fa parte della classe Activity. Questo ha risolto il mio problema.


1

Ho affrontato un problema simile, ecco come ho risolto il mio caso. Verifico se lo taskè già RUNNINGo FINISHEDperché un'attività può essere eseguita una sola volta. Di seguito vedrai un codice parziale e adattato dalla mia soluzione.

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);
    }

}

}

1

Ho avuto lo stesso problema e l'ho risolto. Il mio problema era che stavo usando un listview, con un adattatore di array e con un filtro. Sul metodo performFilteringstavo scherzando con l'array che aveva i dati ed era il problema poiché questo metodo non è in esecuzione sul thread dell'interfaccia utente ed EVENTUALMENTE solleva alcuni problemi.


1

Una causa di questo arresto è che l' ArrayListoggetto non può cambiare completamente. Quindi, quando rimuovo un oggetto, devo fare questo:

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

Questo ha risolto l'incidente per me.


1

Nel mio caso ho chiamato il metodo GetFilter()su un adattatore dal TextWatcher()metodo sull'attività principale e ho aggiunto i dati con un ciclo For attivo GetFilter(). La soluzione consisteva nel cambiare il ciclo For con il AfterTextChanged()metodo secondario nell'attività principale ed eliminare la chiamata aGetFilter()


1
Si prega di chiarire la soluzione. Sono anche nella situazione che sembra u /
nAkhmedov il

1
adapter.notifyDataSetChanged()

2
È buona norma su Stack Overflow aggiungere una spiegazione del perché la soluzione dovrebbe funzionare o è migliore delle soluzioni esistenti. Per maggiori informazioni leggi Come rispondere .
Samuel Liew

0

Inoltre stavo ottenendo lo stesso errore esatto e 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

L'ho risolto inserendo adapter.notifyDataSetChanged();nella parte inferiore del mio thread dell'interfaccia utente, questo è il mio metodo AsyncTask onPostExecute. Come questo :

 protected void onPostExecute(Void aVoid) {

 all my other stuff etc...
    all my other stuff etc...

           adapter.notifyDataSetChanged();

                }

            });
        }

Ora la mia app funziona.

EDIT: In effetti, la mia app continuava a bloccarsi ogni 1 su 10 volte, dando lo stesso errore.

Alla fine mi sono imbattuto runOnUiThreadin un post precedente, che pensavo potesse essere utile. Quindi l'ho inserito nel mio metodo doInBackground, in questo modo:

@Override
protected Void doInBackground(Void... voids) {

    runOnUiThread(new Runnable() {
                      public void run() { etc... etc...

E ho rimosso il adapter.notifyDataSetChanged();metodo. Ora, la mia app non si blocca mai.


0

Prova una di queste soluzioni:

  1. A volte, se aggiungi un nuovo oggetto all'elenco di dati in un thread (o doInBackgroundmetodo), si verificherà questo errore. La soluzione è: creare un elenco temporaneo e aggiungere dati a questo elenco nel thread (o doInBackground), quindi copiare tutti i dati dall'elenco temporaneo all'elenco dell'adattatore nel thread dell'interfaccia utente (o onPostExcute)

  2. Assicurati che tutti gli aggiornamenti dell'interfaccia utente siano chiamati nel thread dell'interfaccia utente.


0

ho riscontrato lo stesso problema quando ho aggiunto nuovi dati nel caricatore di immagini pigro che ho appena inserito

         adapter.notifyDataSetChanged();

nel

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

spero che ti aiuti


0

Come ha detto @Mullins "
Ho aggiunto entrambi gli elementi e chiamato notifyDataSetChanged()nel thread dell'interfaccia utente e ho risolto questo. - Mullins".

Nel mio caso ho asynctaske ho chiamato notifyDataSetChanged()il doInBackground()metodo e il problema è risolto, quando ho chiamato da onPostExecute()ho ricevuto l'eccezione.


0

Avevo un'usanza ListAdaptere stavo chiamando super.notifyDataSetChanged()all'inizio e non alla fine del metodo

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

0

Avevo la stessa seduta, avevo molti buttongroup all'interno del mio oggetto in listview e stavo cambiando alcuni valori booleani all'interno del mio articolo come holder.rbVar.setOnclik ...

il mio problema si è verificato perché stavo chiamando un metodo all'interno di getView (); e stavo salvando un oggetto all'interno di sharepreference, quindi ho avuto lo stesso errore sopra

Come l'ho risolto; Ho rimosso il mio metodo in getView () per notificationDataSetInvalidated () e il problema è andato

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

0

Ho avuto lo stesso problema. finalmente ho ottenuto la soluzione

prima di aggiornare listview, se è presente il tastierino numerico, chiuderlo prima. successivamente imposta l'origine dati e chiama notifydatasetchanged ().

mentre chiudendo la tastiera internamente listview aggiornerà la sua interfaccia utente. continua a chiamare fino alla chiusura della tastiera. quella volta se cambia l'origine dati genererà questa eccezione. se i dati vengono aggiornati in onActivityResult, è possibile che si verifichi lo stesso errore.

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

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

0

La mia soluzione:

1) creare a temp ArrayList.

2) fai i tuoi lavori pesanti (sqlite row fetch, ...) in doInBackground metodo e aggiungi elementi all'arraylist temporaneo.

3) aggiungi tutti gli elementi dall'arylylist temporaneo all'arraylist della tua lista visualizzazione nel onPostExecutemetodo.

note:potresti voler eliminare alcuni elementi da listview e anche eliminare dal database sqlite e forse eliminare alcuni file relativi agli elementi da sdcard, basta rimuovere gli elementi dal database e rimuovere i loro file correlati e aggiungerli all'arraylist temporaneo in background thread. quindi UI threadelimina gli elementi presenti nell'arraylist temporaneo dall'arraylist dell'elenco.

Spero che questo ti aiuti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.