6

I'm having a couple of problems with my AsyncTaskLoader, not sure if they're related as they both happen when attempting to restart the Loader. In my application I have 3 instances of a custom CursorAdapter, backed by 3 instances of a custom AsyncTaskLoader managed by 1 singleton LoaderManager. The problems relate to two differenct Adapter/Loader pairs, but the code used is the same in each case:

getLoaderManager().restartLoader(loaderId, bundle, loaderManager);

Problem 1: I call restartLoader() and the LoaderManager registers a call to onCreateLoader, but not one to onLoaderReset(). The Loader gets to deliverResult(), but onLoadFinished() is never called. The Loader has neither the 'reset' or 'started' flags set (see code below).

Problem 2: I call restartLoader() and the LoaderManager registers a call to onLoaderReset(). The Loader gets to onReset(), but doesn't get any further. The Cursor is set to null, but no new Cursor is loaded.

Any ideas what the problem could be? Here's some of the code for the Loader and Loader Manager:

CustomCursorLoader.java

@Override
protected void onStartLoading() {
    Log.v(TAG, "Starting Loader");
    if (lastCursor != null) {
        deliverResult(lastCursor);
    }
    if (takeContentChanged() || lastCursor == null) {
        forceLoad();
    }
}

@Override
public void deliverResult(Cursor cursor) {
    Log.v(TAG, "Delivering result");
    if (isReset()) {
        Log.v(TAG, "reset");
        if (cursor != null) {
            cursor.close();
        }
        return;
    }
    Cursor oldCursor = lastCursor;
    lastCursor = cursor;
    if (isStarted()) {
        Log.v(TAG, "started");
        super.deliverResult(cursor);
    }
    if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
        oldCursor.close();
    }
}

@Override
protected void onReset() {
    Log.v(TAG, "Reset");
    super.onReset();
    onStopLoading();
    if (lastCursor != null && !lastCursor.isClosed()) {
        lastCursor.close();
    }
    lastCursor = null;
}

CustomCursorLoaderManager.java:

@Override
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
    return new CustomCursorLoader();
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    cursorAdapter.changeCursor(cursor);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
    cursorAdapter.changeCursor(null);
}
Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
Pikaling
  • 8,282
  • 3
  • 27
  • 44

2 Answers2

1

What you are calling a 'LoaderManager' is actually an implementation of the LoaderManager.LoaderCallbacks<D> interface. You might want to use a different name, this one is confusing. Why is it a singleton? It usually is tied to an Activity or Fragment, possibly just the Activity/Fragment implementing the interface. Where are you creating your Loaders (activity/fragment)? Also make sure you call LoaderManager.initLoader() from onCreate()/onActivityCreated(), otherwise the loader may not be started properly.

Nikolay Elenkov
  • 52,576
  • 10
  • 84
  • 84
  • 1. Yeah I know the name's not clear, sorry. 2. It's not a singleton anymore - been working on that part 3. The main problem I'm having is I'd like to be able to use the same Cursor with the same Loader in different Fragments tied to different Activitys – Pikaling Aug 23 '11 at 04:15
  • Why do you want to share cursors? That's probably not a good idea: loaders are managed by activities/fragments, so your loader may be closed when another activity finishes. You can use the same Loader _class_ and have it instantiated in different activities/fragments though. – Nikolay Elenkov Aug 23 '11 at 04:25
  • Thanks for your help. I went through and reworked all my code, all working now. Sometimes I hate OOP... – Pikaling Aug 23 '11 at 16:32
0

When you create a cursor and point it at a database, you can't just set it to null. You have to explicitly close the cursor, or it will lock the database until it times out.

I recommend taking advantage of the Android lifecycle and your existing callbacks to implement this fix.

Hope this helps!

Codeman
  • 12,157
  • 10
  • 53
  • 91
  • Good catch - that should be changeCursor() not swapCursor() - I've changed that now – Pikaling Aug 22 '11 at 21:02
  • are you still having the same problems? – Codeman Aug 22 '11 at 21:09
  • When the load is finished, you should be closing the cursor, not changing or swapping it. You also need to make sure you release it if the app exits or the activity is paused/stopped. – Codeman Aug 22 '11 at 21:24
  • I can't close the cursor - the CursorAdapter needs it. Otherwise none of my ListView's will work – Pikaling Aug 22 '11 at 21:36