Jayesh Salvi | 22 Feb 08:31 2010
Picon

IllegalStateException after ListView's adapter has changed

Hi,

This is regarding the exception:  "java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification....." - seen on Android 1.6+

This problem has been discussed in the past [1][2][3][4], and I have followed all the solutions suggested in those threads. However, few of my users still hit this crash. So I did some source code lookup and have some questions.

Here is the description of what my app is doing:

I use a ListView and populate it with an adapter. The adapter is a direct derivative of BaseAdapter. I populate the list progressively. As the items are downloaded from network they get added to the adapter. The fetching of items is done in doInBackground() and when they are ready to get added to the adapter I invoke publishProgress(), in the onProgressUpdate() method I add new items one-by-one to the adapter. As soon as I add the item to the adapter, I invoke notifyDataSetChanged().

I believe this is as per the best practices that Android developers have suggested in above mentioned threads.

Despite this I get crash reports with this exception. They are rare, but not as rare as ignorable. I myself have hit this crash on my phone/emulator only 1-2 times in last couple of months. The user reported crashes are roughly 1 to 3 per day (approx. at least 600 users use the app per day) (A minority of users may be hitting this crash over and over again)
. This leads me to believe that my adapter update logic is mostly right, but not full proof.

So I dug into the source code of ListView and BaseAdapter.

The exception is thrown because ListView's mItemCount doesn't match the underlying adapter's item count. It happens in layoutChildren() [ListView.java line 1432]
....
             } 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 "
....

So in order to avoid this from happening mItemCount should be updated as soon as the adapter has changed its content. So I searched for locations in the code where mItemCount is updated. I found two locations doing that. In setAdapter() [ListView.java 431] and in onMeasure() [ListView.java 1033]. Consequently, if I am updating the adapter then either setAdapter() or onMeasure() should be executed before the comparision in layoutChildren() takes place, otherwise the exception will be thrown.

My question is, is it possible that my code in onProgressUpdate() that appends to adapter can get executed between onMeasure() and layoutChildren() of the ListView? AFAIU, all three of these methods (onProgressUpdate, onMeasure, layoutChildren) run on the same GUI thread.

I call notifyDataSetChanged immediately after I add to adapter in onProgressUpdate(). I looked into its source code. It calls methods on DataSetObservable and DataSetObserver. I couldn't find how it could directly lead to the updating of mItemCount of the ListView, clearly I don't know much of the internal layout code.

Please let me know what you think. I am trying to understand this mechanism as thoroughly as possible.

Thanks in advance.

Links:
[1] http://www.mail-archive.com/android-developers <at> googlegroups.com/msg60355.html
[2] http://www.mail-archive.com/android-developers <at> googlegroups.com/msg65814.html
[3] http://groups.google.com/group/android-developers/browse_thread/thread/77722caa85f87697
[4] http://groups.google.com/group/android-developers/browse_thread/thread/a451221261cb6a93/2ab5bea015c38437?lnk=gst&q=For+Google+about+BaseAdapter+class#2ab5bea015c38437
[5] ListView.java source code I refered to: http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/java/android/widget/ListView.java;h=7c8151e65132a91aecadd2048fe205bd1c6768a9;hb=HEAD

--
Jayesh

--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-developers <at> googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscribe <at> googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

Gmane