I get 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. [in ListView(2131034188, class android.widget.ListView) with Adapter(class .MainActivity$ListAdapter)]
This is what I do , I run a AsyncTask and get json data , then , onPostExecute, I call ListAdapter that makes the data to listview .
@Override
protected Void doInBackground(Void... arg0) {
Spots_tab1_json sh = new Spots_tab1_json();
String jsonStr = sh.makeServiceCall(url + page, Spots_tab1_json.GET);
if (jsonStr != null) {
JSONObject jsonObj = new JSONObject(jsonStr);
contacts = jsonObj.getJSONArray(TAG_CONTACTS);
for (int i = 0; i < contacts.length(); i++) {
JSONObject c = contacts.getJSONObject(i);
String onvan = new String(c.getString("onvan").getBytes("ISO-8859-1"), "UTF-8");
String id = new String(c.getString("id").getBytes("ISO-8859-1"), "UTF-8");
String dates = new String(c.getString("dates").getBytes("ISO-8859-1"), "UTF-8");
String price = new String(c.getString("gheymat").getBytes("ISO-8859-1"), "UTF-8");
HashMap<String, String> contact = new HashMap<String, String>();
contact.put("onvan", onvan);
contact.put("img", new String(c.getString("img").getBytes("ISO-8859-1"), "UTF-8"));
contactList.add(contact);
}
}
}
}
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (!isCancelled() && goterr == false) {
final ListAdapter ladap=new ListAdapter(MainActivity.this, contactList);
lv.setAdapter(ladap);
runOnUiThread(new Runnable() {
public void run() {
ladap.notifyDataSetChanged();
}});
}
}
and after that, my listView is built. I've tested it on several different devices , non of them had any problem but some users told me and I logged and see this error.
What should I do to solve it ? what is wrong ?
thanks you
Apologies for the belated reply, but it appears as suspected in my earlier comment: you're modifying contactList
from two different threads.
ListView
caches the element count and whenever it has to layout its children, it'll check this count againt the current number of items in the bound adapter. If the count has changed and the ListView
wasn't notified about this, an error is thrown:
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 "
+ "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. [in ListView(" + getId() + ", " + getClass()
+ ") with Adapter(" + mAdapter.getClass() + ")]");
}
Source.
In other words: that's exactly the error you're getting. In your scenario, the count difference is being caused by modifying the backing dataset from more than a single thread, leading to a synchronization issue: the background thread may modify the dataset, get suspended, and the ui thread may then call layoutChildren()
without having been notified about the dataset changes.
Now, onto the solution. The easiest one is to make sure you're not modifying the list that is bound to the ListView
's adapter from different threads. That is, either make a copy that you can then freely modify, or allocate a new list.
So, do something like this, i.e. in the onPreExecute()
method of the AsyncTask
.
New list:
List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>();
A (shallow) copy:
List<HashMap<String, String>> mNewContactList = new ArrayList<HashMap<String, String>>(contactList);
Then, in doInBackground()
, add the data to mNewContactList
. Finally, in onPostExecute()
, you can:
mNewContactList
and set it to the ListView
.mNewContactList
to the already existing contactList
and call notifyDatasetChanged()
on the ListView
.data update and your adapter calls notifyDataSetChanged() must in the same code block. for example:
Correct case:
runOnUiThread(new Runnable() {
@Override
public void run() {
mSelectedImages.add(image);
mAdapter.notifyDataSetChanged();
}
});
Wrong case:
mSelectedImages.add(image);
runOnUiThread(new Runnable() {
@Override
public void run() {
mAdapter.notifyDataSetChanged();
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With