I have a loader. I want it to only start when the underlying data model changes. Which I understood as the point of a loader. From the Android docs:
Loaders, in particular CursorLoader, are expected to retain their data after being stopped. This allows applications to keep their data across the activity or fragment's onStop() and onStart() methods, so that when users return to an application, they don't have to wait for the data to reload.
Great. However, whenever my activity resumes, my loader onStartLoading() is called. Debugging into the platform code, the implementation of Activity.onStart() ends up restarting all of the loaders. Specifically the call stack is,
Activity.onStart() -->
FragmentController..doLoaderStart() -->
FragmentHostCallback.doLoaderStart() -->
LoaderManagerImpl.doStart() -->
LoadermanagerImpl.LoaderInfo.start() -->
Loader.startLoader() -->
<my loader>.onStartLoading()
My loader is costly so I don't want it to reload when my activity is restarted, and this seems to go against the quote above which specifically states that loaders are supposed to retain their data across stops / start cycles.
Does this make sense?
The problem was basically a misunderstanding of loaders. When the framework calls a
Loader'sonStartLoading(), it's more like a lifecycle method. It's the framework telling your loader that the activity has started. Contrary to my original thinking, it doesn't mean that the loader must reload it's data.A naive implementation of a loader (like mine) just reloads all of it's data on
onStartLoading(). A smarter implementation only loads if the underlying data has changed. If we look atCursorLoader.onStartLoading(),It first sends the cached result immediately if it has it. It then calls
forceLoad()which actually loads the data, but it only does this if the data has changed.The other detail is how it tracks when content has changed. It boils down to
Loader's implementation ofonContentChanged():This method says: when the content has changed as we're started, load the data. Otherwise, just keep a flag letting us know the content has changed. It's basically delaying the actual load the of the data until the loader is started.
takeContentChanged()basically checks themContentChangedinstance field:As long as your loader implementation calls
onContentChanged()when the content has changed, the implementation ofLoaderhandles the rest.