Wednesday, July 3, 2013

Don't Override ListView.getAdapter()

A few days we ran into a bug.  When we were updating the underlying data for the particular Adapter, the ListView would blow up with this exception:

E/AndroidRuntime(1809): 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(16908298, class com.expedia.bookings.widget.ItinListView) with Adapter(class android.widget.HeaderViewListAdapter)]

The thing is, we weren't updating from a background thread.  That exception is thrown when the # of items the ListView thinks the adapter has and the # it actually has are out of sync, but I couldn't see how that was happening.  What was going on?

It turned out to be me overriding ListView.getAdapter().  There's some code I've been working with recently which has a custom ListView; inside of it is a custom Adapter.  I wanted access to that Adapter (but not the ListAdapter wrappers that are sometimes added in the case of header/footer views), so I overrided ListView.getAdapter() and had it return the custom Adapter.

However, the ListView uses getAdapter() sometimes to get the count for the # of items it has.  By bypassing the wrapper ListAdapter, the count was sometimes wrong (since it wasn't accounting for header/footer Views).

The moral of the story is: don't override ListView.getAdapter().