Android: LoaderCallbacks.OnLoadFinished called twice

You can put the initLoader() method inside your Fragment's onResume() callback; then the Loader's onLoadFinished() will not be called twice anymore.

    @Override
public void onResume()
{
    super.onResume();
    getLoaderManager().initLoader(0, null, this);
}

This problem manifested itself for me with a CursorLoader returning a Cursor that was already closed:

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

I'd guess this is a bug or an oversight. While moving initLoader() into onResume may work, what I was able to do was remove the Loader when I'm done with it:

To start the loader (in my onCreate):

  getLoaderManager().initLoader(MUSIC_LOADER_ID, null, this);

Then after I'm done with it (basically at the end of onLoadFinished)

  getLoaderManager().destroyLoader(MUSIC_LOADER_ID);

This seems to behave as expected, no extra calls.


initLoader documentation says,

If at the point of call the caller is in its started state, and the requested loader already exists and has generated its data, then callback onLoadFinished(Loader, D)

I suggest you to implement something like onStartLoading function at this sample

For quick test you can try:

@Override protected void onStartLoading() {
    forceLoad();
}

This launch loadInBackground function and then onLoadFinished in Fragment.

Any way, if you attach some code i'll try to give you more help.


I solved the problem of onLoadFinished being called twice like this. In your Fragment.onActivityCreated() init your Loader like this

if (getLoaderManager().getLoader(LOADER_ID) == null) {
    getLoaderManager().initLoader(LOADER_ID, bundle, loaderCallbacks);
} else {
    getLoaderManager().restartLoader(LOADER_ID, bundle, loaderCallbacks);

}

here loaderCallbacks implements your usual Loader callbacks

private LoaderManager.LoaderCallbacks<T> loaderCallbacks
        = new LoaderManager.LoaderCallbacks<T>() {
    @Override
    public Loader<T> onCreateLoader(int id, Bundle args) {
        ...
        ...
    }

    @Override
    public void onLoadFinished(Loader<T> loader, T data) {
        ...
        ...
    }

    @Override
    public void onLoaderReset(Loader<T> loader) {
        ...
        ...
    }
};

The problem is that it called twice:
1. from Fragment.onStart
2. from FragmentActivity.onStart

The only difference is that in Fragment.onStart it checks if mLoaderManager != null. What this means is if you call getLoadManager before onStart, like in onActivityCreated, it will get/create load manager and it will be called. To avoid this you need to call it later, like in onResume.