Reputation: 60681
I have a ListView which potentially contains thousands of rows, generated by a CursorAdapter. Only eight or so rows are visible at any one time. I've had reports that starting up this view can take many seconds, and can cause an ANR (force close).
I'm doing the DB query in a background thread. I've verified that newView
and bindView
in my adapter are only being called for the number of visible rows.
Once the list is displayed, actually scrolling up and down the list is very fast.
The delay is in the call to ListView.setListAdapter
, which has to run on the UI thread. Why does this seem to depend on the total number of rows in the result set, rather than the (much smaller) number of rows which are actually being displayed? Is there any way I can optimize it?
This question was asked a couple of years ago in this thread. I'm hoping to get some fresh insight and more concrete examples of potential workarounds.
UPDATE
I have tried to work around this by using CommonsWare's EndlessAdapter. I limit the initial query to (say) 20 rows using a LIMIT
clause in my DB query, and I increase this limit and resubmit the query every time I hit the bottom of the list.
(As an aside, I haven't found a way of appending just the new results to an existing Cursor, so I'm increasing the LIMIT
value each time and then re-fetching the whole lot up to the new limit in a new Cursor.)
Strangely, this workaround doesn't seem to improve the time it takes to perform the initial setListAdapter
call. When I run it on a data set containing only 20 rows, the call to setListAdapter
is really quick. When I run it on a data set containing hundreds of rows, but limited to return just 20, it takes over a second.
UPDATE 2
By forcing the query to execute in the background thread with a simple getCount()
, as suggested by CommonsWare, I've cured the initial blocking of the UI thread on starting the activity. The UI is still blocked, though, when returning to this activity from a child activity. The ListActivity
by default seems to want to re-run the query on the UI thread.
I have worked around this by removing the adapter in onStop()
, and recreating it in onStart()
. Thus the query is always performed in the background regardless of the direction we're moving through the activity stack.
Upvotes: 0
Views: 1431
Reputation: 1006594
I'm doing the DB query in a background thread
If all you do in the background is call query()
or rawQuery()
, the query is not actually executed. It will be lazy-executed when you first try using the Cursor
(e.g., getCount()
). So, the right recipe for doing a query in the background is a rawQuery()
followed by something like getCount()
in the background thread, to ensure the query really is executed.
When I run it on a data set containing hundreds of rows, but limited to return just 20, it takes over a second.
Off the cuff, that would suggest that the speed issue is not the time required to read in the results, but rather in computing the results in the first place.
Upvotes: 4