Using a Cursor returned from a LoaderManager in an AsyncTask
Asked Answered
J

2

6

I have a cursor returned on an onLoadFinished callback (from LoaderManager.LoaderCallbacks), and I want to do some (possibly costly) post-processing on this cursor. So, I'm firing off an AsyncTask that uses this Cursor. However, I'm getting intermittent crashes with this exception:

android.database.StaleDataException: Attempted to access a cursor after it has been closed.

My suspicion is that this is happening because the cursor (being managed by the Loader in the UI Thread) is being closed before the background thread is finished with it, since this is a managed cursor. Here is some paraphrased code:

private class LoaderListener implements LoaderManager.LoaderCallbacks<Cursor> {
    @Override
    public void onCreateLoader(int d, Bundle args) {
        return new CursorLoader(context, uri, projection, selection, selectionArgs, sortOrder);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        processCursor(cursor)    
    }
}

private void processCursor(final Cursor cursor) {
    new AsyncTask<Void, Void, Result> {
        @Override
        Result doInBackground(Void... params) {
            while(cursor.isAfterLast() == false) {
                // doing some costly things with cursor
            }
        }
    }.execute();
}

Is it possible to either,

  1. Somehow flag the cursor to prevent it from being closed from the UI thread.

  2. Notify the manager that the cursor is still in use.

  3. Clone it so that the cloned instance doesn't get closed by the manager.

  4. Another, even better, solution?

Having this post-processing done on the UI thread is absolutely not an option, however, as it can be very costly.

Justiceship answered 8/6, 2012 at 23:49 Comment(2)
You could always skip the CursorLoader and make the initial query + the expensive processing in the AsyncTask.Jacquez
are you using this cursor for something else?Pyrophosphate
S
3

Is it possible to somehow flag the cursor to prevent it from being closed from the UI thread?

No (well, not without re-writing the internal APIs).

Is it possible to notify the manager that the cursor is still in use?

Same answer as above.

Is it possible to clone it so that the cloned instance doesn't get closed by the manager?

This sounds kind of messy... and there is still the chance that the LoaderManager closes the cursor before you are able to finish cloning it.

Is there a better solution?

Yes. Query a new cursor instead of trying to reusing the one that you pass to the LoaderManager.

Seneschal answered 9/6, 2012 at 0:10 Comment(2)
Can you be more specific? From my understanding of your answer, I'd still have to post-process the new Cursor, leading to the same problem... ?Justiceship
May I ask what it is you are trying to do? Why is it that your "post-processing" is tied together w/ the LoaderManager lifecycle? Why not just treat them separately? It'd be a lot cleaner that way...Seneschal
L
0

Man, I am facing the very same problem. The way is to cancel the asynctask in the activity's onDestroy method.

private YourAsyncTask asyncTask

@Override
protected void onDestroy(){
    super.onDestroy();
    asyncTask.cancel(true);
}
Longitude answered 16/5, 2014 at 17:52 Comment(1)
I face exactly the same issue as Tshepang. None of the responses actually provide a solution. The suggestion to "query another cursor" seems somehow absurd. I suppose you could build a MatrixCursor from the original which is some form of cloning. Cancelling the AsyncTask does not make sense either.Stelu

© 2022 - 2024 — McMap. All rights reserved.