CWAC-LoaderEx and Failed Abstractions
Loader framework showed up in Android 3.0, my initial interest was
tempered by the fact that there was only one concrete
provided by Android. That was
CursorLoader, and it required a
as the backing store for the data. After the umpteenth question on StackOverflow
about trying to use
CursorLoader without a
ContentProvider, I elected to take
a shot at providing other concrete
Loader implementations. Specifically, I created
the CWAC-LoaderEx library, with three such
SharedPreferencesLoader(because initially loading preferences involves disk I/O)
CursorLoader, but for working directly with SQLite)
SQLCipherCursorLoader(same as above, but for SQLCipher for Android)
All of them sucked, and so I never used them. Since I never used them, I never maintained them. And, since I never maintained them, I have officially discontinued the project.
This is not to say that
CursorLoader is a fine solution for
three common problems:
How do we retrieve data in the background?
How do we retain that data across configuration changes?
How do we arrange to get updated data when the data changes elsewhere in the app?
The first two are handled mostly by the framework itself, through classes like
AsyncTaskLoader. Anyone else implementing an
will get those two features “for free”, more or less.
The third one is the sticking point and is why my loaders sucked.
The framework, on its own, has little means of determining when some arbitrary data changes.
Hence, the implementation of this mostly is up to the
Loader, with minimal framework
The problem is that the interesting cases for this feature is where the data is
changed in some random spot in your app. For example, a service might update
a database. Or some other activity (like a
PreferenceActivity) might update a
This begs the question: how does a
Loader, attached to a
find out when data of interest changes anywhere in the app?
In the case of
SharedPreferences, there is a listener. However, the
instance is not replaced when it is updated – rather, it is updated in situ. This
runs afoul of an optimization inside of the
Loader framework, where it is assumed
that data changes are reflected in object instance changes, such as where a new
replaces an old
Cursor. Hence, my
SharedPreferencesLoader never really provided
SharedPreferences when another part of the app updated those preferences.
In the case of a database, we do not even have an Android-supplied listener. Instead,
our code that updates the database would have to somehow let the
Loader know about
those updates. For simplistic cases, such as the
Activity hosting the
its own database manipulation, this is easy. For cases where the data-updating component
has no access to the
Loader, this is difficult.
Loader framework effectively assumes that all implementations of loaders:
- Have singleton manager objects (like a
- Have distinct data objects per update (like a
Anything not meeting those requirements does not fit.
Ideally, when the
Loader framework was introduced, three or more concrete implementations
would have gone into the Android SDK. The act of creating those implementations would
have put stress on the
Loader design, probably highlighting the aforementioned implicit
requirements. That, in turn, might have changed the design. As it stands, I consider
Loader to be a bit of a failed abstraction – it works very well for the one concrete
implementation and is rather awkward for everything else that I have seen or tried.
So, feel free to use
CursorLoader, if you wish to load data from a
whether that is one you wrote or one provided by somebody else (e.g.,
And, if you happen to have an environment where a custom
Loader really can fulfill
all the requirements, explicit and implicit, feel free to do so.
In my case, if I am going to have some singleton manager object, with distinct
data objects per operation, I am going to use something more flexible than
as an event bus.
Need an extra hand with your Android app development project? CommonsWare can help — reach out for details!