Nicely done.
Simon Zettervall
51

Good questions about Loaders all around! I’ll see if I can answer each in turn.

Why is the caching not built in by default? Why does the framework not handle this for us?

Generally, anything that is built in by default is fixed behavior and reduces the cases that can be handled by the API. Let’s say, for instance, that whatever you call to deliverResult() is automatically cached for you. Implementation wise, this would be exactly as we do it in our example — a member variable of the Loader (remember, it is the Loader object that is retained across configuration changes) and saves us roughly ~2 lines of code.

However now we have a question on what onStartLoading() should do — does it automatically redeliver cached results? That’s how all three of my examples worked, but might not be the case for 100% of Loaders. A counter-example might be our third Loader wanting to ensure a certain freshness of Location data — redelivering a 30 minute old location might lead to a map shift that would be disorienting (particularly when tied to a new location coming in).

And, in API design, you really only get one shot — any changes here would break existing Loaders or at least lead to edge cases like double delivering results and having the LoaderCallbacks be unprepared for that case.

You want to use the Loader because it handles the configuration changes nicely, and this is the sole purpose of using it.

It is a big reason, definitely, but it also forces you to separate your concerns. Having an Activity/Fragment handle everything and interact directly with each data source is a good way to build something totally untestable — even without their configuration change properties I’d suggest using Loaders as an interface to your data.

Is there any reason for the framework to not cache the result? Providing an invalidate method that would clear the cached result would remove the argument of needing “stateless” loaders or just clearing the cache.

Well, ‘the framework’ knows nothing about your data, your Loader does. Therefore the responsibility does seem to lay on your Loader to be the owner of the data. I’m not against a CachingLoader and a CachingAsyncTaskLoader, but I do think it would be odd if each were caching the result while you still needed to manage caching other information (see CursorLoader’s source code for an example of a Loader that needs more than its result cached).

Another thing that bugs me is that the method “onStartLoading()” explicitly says “Subclasses must implement this”, and it is easy to miss this overriding part the first times one implement the loaders. Having a simple abstract before would force the developer to implement this.

I totally agree that this is one of the hardest parts for people new to Loaders/AsyncTaskLoaders. I’ve definitely run into very good developers who would call initLoader() followed by forceLoad(), not knowing that is what onStartLoading() should probably be doing and they were actually not using the caching they were expecting to get.

Changing methods to abstract, particularly for a public API, however, is a breaking change. If done in the framework Loader, this would break every Loader that does not implement the method — and forward compatibility is a very serious thing on Android. The support version of Loader is perhaps more amenable to change (since developers would need to manually update), but maintenance is much easier if the framework and support versions stay perfectly in sync.

Your best shot?

Reshare the original article. Correct others when they make mistakes. Publish your own helper classes into a library other developers can use in their own apps — you’ll be able to see 1) exactly how much interest there is in that problem space and 2) help other developers get started the right way and #BuildBetterApps