Android: LoaderCallbacks.OnLoadFinished called twice

I noticed a strange situation with Android Loaders and Fragments. When I call LoaderManager.initLoader () after changing the orientation, onLoadFinished is not called (although the documentation suggests that I should be prepared for this), but it is called twice after that. Here is a link to a post in google groups that describe the same situation https://groups.google.com/forum/?fromgroups#!topic/android-developers/aA2vHYxSskU . I wrote an example application in which I only run a simple loader in Fragment.onActivityCreated () to check if this has happened and it will. Has anyone noticed this?

+51
android loader
Jul 02 2018-12-12T00:
source share
9 answers

You can put the initLoader () method inside the callback Fragment onResume (); then Loader onLoadFinished () will no longer be called twice.

@Override public void onResume() { super.onResume(); getLoaderManager().initLoader(0, null, this); } 
+36
Jan 25 '13 at 14:40
source share

This problem appeared to 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 assume this is a mistake or oversight. Although moving initLoader () to onResume might work, what I was able to do was remove Loader when I was done with it:

To start the bootloader (in my onCreate):

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

Then after I finished with it (mostly at the end of onLoadFinished)

  getLoaderManager().destroyLoader(MUSIC_LOADER_ID); 

This is similar to the behavior as expected, no additional calls.

+21
Mar 04 '14 at 21:17
source share

initLoader documentation

If the calling subscriber is in a running state and the requested bootloader already exists and generated its data, then callback onLoadFinished (Loader, D)

I suggest you implement something like the onStartLoading function in this sample

For quick testing, you can try:

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

This function starts loadInBackground, and then onLoadFinished in the fragment.

In any case, if you attach some code, I will try to give you more information.

+6
Dec 17 '12 at 21:41
source share

I solved the onLoadFinished problem, called twice that way. In your Fragment.onActivityCreated (), run 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) { ... ... } }; 
+3
Mar 06 '16 at 7:10
source share

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

The only difference is that in Fragment.onStart it checks if mLoaderManager! = Null. This means that if you call getLoadManager before onStart, for example in onActivityCreated, it will receive / create a load manager and it will be called. To avoid this, you need to call it later, for example, in onResume.

+2
May 5 '14 at 10:18
source share

When calling initLoader from onActivityCreated you can detect a rotation:

 @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (savedInstanceState == null) { // fresh new fragment, not orientation/config change getLoaderManager().initLoader(YOUR_LOADER_ID, null, mCallbacks); } ... } 

Thus, the loader behaves as expected, resulting in a single call to onLoadFinished .
It is no longer called for rotation, so if you want loader data, you can save it in your fragment, for example, by overriding onSaveInstanceState .

Edit:
I just realized that onLoadFinished will not be called if rotation occurs during loadInBackground loading. To fix this, you still need to call initLoader after rotation if the data from the bootloader is not yet available.

Hope this helps.

+2
Feb 04 '15 at 10:39
source share

You can also compare the data object in onLoadFinished (loader loader, object data). If the data object matches the one you already have, you can just do nothing when onLoadFinished is called. For example:

 public void onLoadFinished(Loader loader, Object data) { if(data != null && mData != data){ //Do something } } 
+1
Feb 16 '15 at 1:58
source share

Since all searches for this topic inevitably end here, I just wanted to add my experience. As @jperera said, the culprit was that the LoaderManager would call onLoadFinished () if the loaders already exist. In my case, I had fragments in the FragmentPager and scrolling 2 tabs, and then scrolling next to it would again make my old fragment start to create itself.

Since placing initLoader () inside onCreate () also causes double callbacks, I placed initLoader () inside onResume (). But the sequence of events ends with the onCreate (), LoaderManager calling the callbacks since there are loaders, then onResume () is called, calling the next sequence initLoader () and onLoadFinished (). IE, another double callback.

Decision

I found a quick solution to "Matt" . After all your data has been loaded (if you have more than one bootloader), destroy all bootloaders so that their callbacks are not called extra time.

0
Feb 16 '16 at 21:38
source share

I have this problem. But I used to call destroyloader(YOUR_ID) in the loaderfinished methods. then the bootloader does not call the backgrdound task again twice.

0
Jun 14 '16 at 5:52
source share



All Articles