tags:

views:

860

answers:

2

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);
    }
};
+1  A: 

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.

CommonsWare
Before I added the changeCursor call, everything was happy and ran in the background. Once I added this: updateHandler.post(new Runnable() { public void run() { listAdapter.changeCursor(cur); } });Everything went to crap fast.
MattC
Odd question: what is filterLock in your above code? Are you possibly running into a deadly embrace or deadlock?
CommonsWare
It's just a lock that we had anticipated using in case the user kept typing while the first query was running.Stepping through the actual code of the CursorAdapter, changeCursor calls notifyDataSetChanged() and this is where it's hanging.
MattC
Also note, I tried modifying my code to create an all-new listAdapter but that too hangs in the UI thread.
MattC
I see this answer was marked as the solution. What was the final verdict on this?
hambonious