LoaderCallbacks.onLoadFinished unnecessarily called twice when rotating

Showing 1-5 of 5 messages
LoaderCallbacks.onLoadFinished unnecessarily called twice when rotating Malcolm Evershed 4/11/12 3:05 PM

[My apologies if this is posted twice, it has been a few days and my original post didn't show up yet, so I'm attempting to post again]


I think I've found an issue where LoaderManager.LoaderCallbacks<D>.onLoadFinished() will be called unnecessarily twice when rotating the device. Is this expected behavior or should I open a bug?

 

This can easily be reproduced by running ApiDemos -> App -> Loader -> Cursor (which corresponds to the LoaderCursor.java example) and rotating the device.

 

1) Here's the first location where onLoadFinished() is called:

 

LoaderCursor$CursorLoaderListFragment.onLoadFinished(Loader, Cursor) line: 159        

LoaderCursor$CursorLoaderListFragment.onLoadFinished(Loader, Object) line: 1        

LoaderManagerImpl$LoaderInfo.callOnLoadFinished(Loader, Object) line: 438        

LoaderManagerImpl$LoaderInfo.reportStart() line: 318        

LoaderManagerImpl.doReportStart() line: 778        

LoaderCursor$CursorLoaderListFragment(Fragment).performStart() line: 1534        

FragmentManagerImpl.moveToState(Fragment, int, int, int) line: 862        

FragmentManagerImpl.moveToState(int, int, int, boolean) line: 1032        

FragmentManagerImpl.moveToState(int, boolean) line: 1014        

FragmentManagerImpl.dispatchStart() line: 1771        

LoaderCursor(Activity).performStart() line: 4481        

ActivityThread.performLaunchActivity(ActivityThread$ActivityClientRecord, Intent) line: 1929        

ActivityThread.handleLaunchActivity(ActivityThread$ActivityClientRecord, Intent) line: 1981        

ActivityThread.handleRelaunchActivity(ActivityThread$ActivityClientRecord) line: 3351        

ActivityThread.access$700(ActivityThread, ActivityThread$ActivityClientRecord) line: 123        

ActivityThread$H.handleMessage(Message) line: 1151        

ActivityThread$H(Handler).dispatchMessage(Message) line: 99        

Looper.loop() line: 137        

ActivityThread.main(String[]) line: 4424        

Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]        

Method.invoke(Object, Object...) line: 511        

ZygoteInit$MethodAndArgsCaller.run() line: 784        

ZygoteInit.main(String[]) line: 551        

NativeStart.main(String[]) line: not available [native method]        

 

Basically, as the Activity is starting up, Activity.performStart() calls mFragments.dispatchStart() which eventually calls Fragment.performStart(), which then calls mLoaderManager.doReportStart(). Ultimately, LoaderManagerImpl$LoaderInfo.reportStart() checks if the loader has been started and whether mReportNextStart is set (it was set when Fragment.performDestroyView() was called on the old fragment) and then it'll call onLoadFinished().

 

2) Here's the second location where onLoadFinished() is called during the same rotation:

 

LoaderCursor$CursorLoaderListFragment.onLoadFinished(Loader, Cursor) line: 159        

LoaderCursor$CursorLoaderListFragment.onLoadFinished(Loader, Object) line: 1        

LoaderManagerImpl$LoaderInfo.callOnLoadFinished(Loader, Object) line: 438        

LoaderManagerImpl$LoaderInfo.finishRetain() line: 309        

LoaderManagerImpl.finishRetain() line: 765        

LoaderCursor(Activity).performStart() line: 4485        

ActivityThread.performLaunchActivity(ActivityThread$ActivityClientRecord, Intent) line: 1929        

ActivityThread.handleLaunchActivity(ActivityThread$ActivityClientRecord, Intent) line: 1981        

ActivityThread.handleRelaunchActivity(ActivityThread$ActivityClientRecord) line: 3351        

ActivityThread.access$700(ActivityThread, ActivityThread$ActivityClientRecord) line: 123        

ActivityThread$H.handleMessage(Message) line: 1151        

ActivityThread$H(Handler).dispatchMessage(Message) line: 99        

Looper.loop() line: 137        

ActivityThread.main(String[]) line: 4424        

Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]        

Method.invoke(Object, Object...) line: 511        

ZygoteInit$MethodAndArgsCaller.run() line: 784        

ZygoteInit.main(String[]) line: 551        

NativeStart.main(String[]) line: not available [native method]        

 

Basically, Activity.performStart() calls LoaderManagerImpl.finishRetain() (shortly after calling mFragments.dispatchStart() in the first callstack above). Ultimately, LoaderManagerImpl$LoaderInfo.finishRetain() checks if the loader has been started and whether mReportNextStart is cleared (it was just cleared by LoaderManagerImpl$LoaderInfo.reportStart() in the first callstack above) and then it'll call onLoadFinished().

 

 

Here is my guess of what is really going on during a rotation:

  • Activity.performStart() ultimately causes this sequence of calls:
    • LoaderManagerImpl$LoaderInfo.reportStart() (from mFragments.dispatchStart())
    • LoaderManagerImpl$LoaderInfo.finishRetain() (from LoaderManagerImpl.finishRetain())
    • LoaderManagerImpl$LoaderInfo.reportStart() (from LoaderManagerImpl.doReportStart())
  • LoaderManagerImpl$LoaderInfo.reportStart() and LoaderManagerImpl$LoaderInfo.finishRetain() use the mReportNextStart flag to determine whether to call onLoadFinished(), but this flag usage isn't designed for the sequence of calls above. It seems like maybe 2 flags may be required to cover all the possibilities of call sequences.

 

But that's just my guess, I'm new to this code. :)


Thanks.

Re: LoaderCallbacks.onLoadFinished unnecessarily called twice when rotating szakalinhoPL 7/2/12 5:17 AM
Hi, I noticed same situation,  I'm not an expert but it seems like a bug because behaviour of onLoadFinished is not normal according to documentation which says that onLoadFinished should be called in initLoader, but it never does. In very simple application it happens as well. Did you report it or find out an answer?
Re: [android-developers] Re: LoaderCallbacks.onLoadFinished unnecessarily called twice when rotating Malcolm Evershed 7/2/12 1:49 PM
Your issue sounds like it could be different than mine since I'm generally seeing onLoadFinished() called in initLoader(). You might want to do more web or StackOverflow searching or search the Android bug database -- I think somewhere I read about a problem where onLoadFinished() would not be called, but I think it only happens if you don't do the stuff that CursorLoader does. In other words, to solve your issue, you might have to copy a bunch of code from CursorLoader.

As to my issue, I never reported it since there didn't seem to be any interest in it. I just worked around it in my app.

Thanks.

--
You received this message because you are subscribed to the Google
Groups "Android Developers" group.
To post to this group, send email to android-d...@googlegroups.com
To unsubscribe from this group, send email to
android-developers+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/android-developers?hl=en

unk...@googlegroups.com 7/8/12 5:59 PM <This message has been deleted.>
Re: [android-developers] Re: LoaderCallbacks.onLoadFinished unnecessarily called twice when rotating Matt 3/4/14 1:20 PM
There's a fair discussion on StackOverflow here:
http://stackoverflow.com/questions/11293441/android-loadercallbacks-onloadfinished-called-twice/22183247

I saw the behavior when re-loading a Fragment in a ViewPager, which may be very similar to rotation... Basically, I solved the problem by removing the loader when I was done with it: getLoaderManager().destroyLoader(LOADER_ID);