I use Cursors
extensively in my app, to load and occasionally write information from and to a database. I have seen that Honeycomb and the Compatibility Package have new Loader
classes designed to help with loading data in a "good" way.
Essentially, are these new classes (in particular CursorLoader
) considerably better than previous methods of managing data? What is the benefit of a CursorLoader
over managed Cursors
for example?
And I use a ContentProvider
to deal with data, which obviously takes Uris
but how does this mesh with the initLoader()
method? Must I set up each of my Fragments
to use Loaders individually? And how unique does the id need to be for each loader, is it over the scope of my app or just a fragment? Is there any simple way of simply passing a Uri
to a CursorLoader to query my data?
All I can see at the moment is that Loaders add an unnecessary extra step to getting my data into my app, so can someone explain them to me better?
There are two key benefits to using a
CursorLoader
in your app overActivity.managedQuery()
:AsyncTaskLoader
) so large data queries do not block the UI. This is something the docs recommended you do for yourself when using a plainCursor
, but now it's done under the hood.CursorLoader
is auto-updating. In addition to performing the initial query, theCursorLoader
registers aContentObserver
with the dataset you requested and callsforceLoad()
on itself when the data set changes. This results in you getting async callbacks anytime the data changes in order to update the view.Each
Loader
instance is also handled through the singularLoaderManager
, so you still don't have to manage the cursor directly, and now the connection can persist even beyond a singleActivity
.LoaderManager.initLoader()
andLoaderManager.restartLoader()
allow you to reconnect with an existingLoader
already set up for your query and, in some cases, instantly get the latest data if it is available.Your
Activity
orFragment
will likely now implement theLoaderManager.Callback
interface. CallinginitLoader()
will result in theonCreateLoader()
method where you will construct the query and a newCursorLoader
instance, if necessary. TheonLoadFinished()
method will be fired each time new data is available, and will include the latestCursor
for you to attach to the view or otherwise iterate through.In addition, there is a pretty good example of all this fitting together on the
LoaderManager
class documentation page: http://developer.android.com/reference/android/app/LoaderManager.htmlHope that Helps!
If anyone finds themselves in a similar situation, here's what I've done:
LoaderCallbacks
and handles all the queries you'll need.Context
and theAdapter
in question.UriMatcher
, might as well use the same ones)LoaderCallbacks
In my
GlobalCallbacks
class:And when I wanted to use a
CursorLoader
(Helper.bundleArgs()
is the convenience bundling method):And in Helper:
Hope this helps someone else :)
EDIT
To explain more thoroughly:
Cursor
is initialised. We don't supply it with aCursor
becauseGlobalCallbacks
will give the adapter the correctCursor
inonLoadFinished(..)
LoaderManager
we want to initialise a newCursorLoader
. We supply a newGlobalCallbacks
instance (which implementsLoader.Callbacks
) which will then monitor the loading of the cursor. We have to supply it with the adapter too, so it can swap in the newCursor
once its done loading. At some point, theLoaderManager
(which is built into the OS) will callonCreateLoader(..)
ofGlobalCallbacks
and start asynchronously loading dataHelper.bundleArgs(..)
just puts arguments for the query into aBundle
(e.g. columns projection, sort order, WHERE clause)Fragment
'sListAdapter
. The cursor will still be null at this point, so it will show a loading sign or empty message untilonLoadFinished()
is called