*[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.

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

Reply via email to