简体   繁体   中英

listView crashed randomly when refresh

I have a listView whixh is refresh each time the client (my android app) receive a message from the server but the problem is when i click to many time on the refresh button the app crash and send me this error: 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. Make sure your adapter calls notifyDataSetChanged() when its content changes. 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. Make sure your adapter calls notifyDataSetChanged() when its content changes.

edit 1: the crash does not happen every time it onlu happen when i click about ten times quickly on the refresh button

activity code:

 class receiveimplements Runnable {
    @Override
    public void run() {
        try {

            if (socket != null && socket.isConnected()) {

                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                for (; ; ) {
                    while ((textRecu = reader.readLine()) != null) {
                        if (textRecu.length() < 6) {
                            bug = true;
                            break;
                        }

                        Log.d("textrecuapres", textRecu);
                        index++;
                        if (index == 1) {
                            listTitre.add(0, textRecu);
                            sendListeAndMakeAffichage(listSource,listTitre);
                        } else if (index == 2) {
                            listSource.add(0, textRecu);
                            index = 0;
                        }
                    }
                }
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

public void sendListeAndMakeAffichage(final List<String> sourceList,final List<String> sourceTitre) {

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            fragmentListe.setListTitreAndListSource(sourceTitre, sourceList);

        }
    });

}

Listfragment code:

public void setListTitreAndListSource(List<String> listTitre, List<String> listSource) {

    this.listTitre = listTitre;
    this.listSource = listSource;

    adapter.bind(listTitre);
    setListAdapter(adapter);
}

adapter code:

  public void bind(List<String> model) {
    notifyDataSetChanged();
    listeTitre = model;
    notifyDataSetChanged();
}

When you add to listTitre , without calling notifyDataSetChanged()

  if (index == 1) {
     listTitre.add(0, textRecu);
     sendListeAndMakeAffichage(listSource,listTitre);

This is changing the contents of the adapter. You do schedule a call to notifyDataSetChanged() however it is not guaranteed to be scheduled before the ListView's render which will validate it's state. The adapter has to be manipulated within the same handled message as the notifyDataSetChanged() call in order to guarantee it all happens at once in the next render.

So right now you are doing:

Thread-1: Add to List

Main Thread : evaluatePendingMessages([validate ListView, runnable which calls notifyDataSetChanged() ]

You have to modify the adapter within the same event as the part that updates the adapter (And they both have to root from the main thread)

So if this is valid with your logic you should do this.

  if (index == 1) {
     sendListeAndMakeAffichage(listSource,listTitre, textRecu);
  } ....

Then inside of that method:

public void sendListeAndMakeAffichage(final List<String> sourceList,final List<String> sourceTitre, final String newEntry) {

    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            listTitre.add(newEntry); // Now we manipulate the list in this runnable
            // While the next call to the fragment will finish updating the adapter
            // and call notifyDataSetChanged()
            fragmentListe.setListTitreAndListSource(sourceTitre, sourceList);

        }
    });

}

As the error message informs you, you cannot modify UI elements from a non-UI thread (and your Runnable uses a separate thread).

Use a Handler to update the UI thread. See this answer, for example: https://stackoverflow.com/a/12717105/240646

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM