Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: LoaderCallbacks.OnLoadFinished called twice

Tags:

android

loader

I noticed strange situation using Android Loaders and Fragments. When I invoke LoaderManager.initLoader() after orientation change onLoadFinished is not called (although documentation suggests I should be prepared for this) but it is called twice after this. Here is link to post in google groups which describe the same situation https://groups.google.com/forum/?fromgroups#!topic/android-developers/aA2vHYxSskU . I wrote sample application in which I only init simple Loader in Fragment.onActivityCreated() to check if this happens and it does. Anyone noticed this?

like image 305
LukaszS Avatar asked Jul 02 '12 12:07

LukaszS


4 Answers

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);
}
like image 94
Bogdan Zurac Avatar answered Nov 06 '22 17:11

Bogdan Zurac


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.

like image 32
Matt Avatar answered Nov 06 '22 17:11

Matt


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.

like image 6
jperera Avatar answered Nov 06 '22 16:11

jperera


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) {
        ...
        ...
    }
};
like image 5
Amrendra Kumar Avatar answered Nov 06 '22 16:11

Amrendra Kumar