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