I'm calling an ASyncTask
to update my adapter, and once in a while I'm getting this runtime error when it's populating my list
E/AndroidRuntime(16197): 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.
Is it a bad idea to populate my adapter from a background thread, like the error message implies?
I'm calling notifyDataSetChanged()
in onPostExecute()
, but I also get the same error when I call it in onProgressUpdate()
.
Here's my ASyncTask. Assume fetchOneItem() fetches a single item out of a list of items, fills out the name/description, and moves on to the next item in the next iteration.
private class GetItems extends AsyncTask<Integer, Integer, Integer> {
@Override
protected void onPreExecute() {
super.onPreExecute();
pd = new ProgressDialog(context);
pd.setTitle("Displaying...");
pd.setMessage("Please wait.");
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();
TextView myMsg = (TextView)findViewById(R.id.topMsg);
myMsg.setText("Displaying items...");
}
protected Integer doInBackground(Integer... params) {
// TODO Auto-generated method stub
do {
fetchOneItem(); // fetches 1 item
myList.add(new Item(name, description));
Log.i(TAG, "Adding " + name + ", " + desc + ", adapter size = " + myAdapter.getCount());
publishProgress(i);
} while (doneFetching != true);
Log.i(TAG, "Completed loading, i =" + i);
return i;
}
protected void onProgressUpdate(Integer... values) {
TextView myMsg = (TextView)findViewById(R.id.topMsg);
myMsg.setText("Displaying " + Integer.toString(values[0].intValue()) + " items");
}
@Override
protected void onPostExecute(Integer numItems) {
TextView myMsg = (TextView)findViewById(R.id.topMsg);
myMsg.setText("Displayed " + numItems + " items");
myAdapter.notifyDataSetChanged();
allItemsLoaded = true;
Log.i(TAG, "Adapter size = " + myAdapter.getCount());
pd.dismiss();
}
}
Yes, as you can see android doesn't allow you to do this. If you want to update any view you should do it in the UI thread. But you should also never use slow operations in the UI thread such as network communcation which is isn't allowed in android. In fact you should keep the UI code as short as you can because if it takes too long then android will think your app has crashed.
Edit: I think you are using a ArrayAdapter which will notify changes when you update it.
So you got two choices:
Use your ArrayAdapter but update it in the UI thread.
Or implement your own subclass of BaseAdapter and you were doing it right.
ArrayAdapter is intended for simple situations, often with static data set.