Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android CursorAdapters, ListViews and background threads

Tags:

java

android

This application I've been working on has databases with multiple megabytes of data to sift through. A lot of the activities are just ListViews descending through various levels of data within the databases until we reach "documents", which is just HTML to be pulled from the DB(s) and displayed on the phone. The issue I am having is that some of these activities need to have the ability to search through the databases by capturing keystrokes and re-running the query with a "like %blah%" in it. This works reasonably quickly except when the user is first loading the data and when the user first enters a keystroke. I am using a ResourceCursorAdapter and I am generating the cursor in a background thread, but in order to do a listAdapter.changeCursor(), I have to use a Handler to post it to the main UI thread. This particular call is then freezing the UI thread just long enough to bring up the dreaded ANR dialog. I'm curious how I can offload this to a background thread totally so the user interface remains responsive and we don't have ANR dialogs popping up.

Just for full disclosure, I was originally returning an ArrayList of custom model objects and using an ArrayAdapter, but (understandably) the customer pointed out it was bad memory-manangement and I wasn't happy with the performance anyways. I'd really like to avoid a solution where I'm generating huge lists of objects and then doing a listAdapter.notifyDataSetChanged/Invalidated()

Here is the code in question:

private Runnable filterDrugListRunnable = new Runnable() {
    public void run() {
        if (filterLock.tryLock() == false) return;

        cur = ActivityUtils.getIndexItemCursor(DrugListActivity.this);

        if (cur == null || forceRefresh == true) {
            cur = docDb.getItemCursor(selectedIndex.getIndexId(), filter);
            ActivityUtils.setIndexItemCursor(DrugListActivity.this, cur);
            forceRefresh = false;
        }

        updateHandler.post(new Runnable() {
           public void run() {
               listAdapter.changeCursor(cur);
           }
        });

        filterLock.unlock();

        updateHandler.post(hideProgressRunnable);
        updateHandler.post(updateListRunnable);
    }
};
like image 481
MattC Avatar asked Sep 18 '26 13:09

MattC


1 Answers

I find it difficult to believe that listAdapter.changeCursor() alone would take up sufficient time to cause an ANR, assuming you created the Cursor in a background thread. There just should not be that much work that needs to happen to repaint a handful of list rows. I would double-check to see what work you are doing in your Handler is as limited as you think. Perhaps consider using an AsyncTask, which makes it easier to separate the background work (doInBackground()) from the on-UI-thread post-processing (onPostExecute()).

You can try just replacing the adapter outright, by calling setAdapter() again with the new Cursor wrapped in a new adapter.

You can look to see how AutoCompleteTextView handles this scenario, since it does on-the-fly filtering with a SpinnerAdapter. Perhaps some of its techniques can apply in your case.

like image 68
CommonsWare Avatar answered Sep 21 '26 04:09

CommonsWare



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!