How does CursorLoader with LoaderManager know to send the cursor to a CursorAdapter?
First of all, check out the code sample at this post and this post for an even more in-depth look into how the process works.
And now, to answer your questions...
How does the query() method from my custom
ContentProvider
...?
Well, first remember that getContentResolver().query()
doesn't invoke the content provider's query
method directly. You are invoking the content resolver's query method, which parses the Uri
, determines the provider you wish to invoke, and then calls your provider's query
method.
How does the query get sent to that specific
CursorAdapter
?
I'll walk you through the process using the API Demos as an example. Note that the API demos uses a ListFragment
instead of a ListActivity
(the difference is not important in the context of this question).
-
First, create (and set up) the
CursorAdapter
.mAdapter = new SimpleCursorAdapter( getActivity(), android.R.layout.simple_list_item_2, null, new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, new int[] { android.R.id.text1, android.R.id.text2 }, 0);
After this statement is executed, the
SimpleCursorAdapter
knows how it should associate the cursor data with your views. Whatever data is in the cursor'sContacts.DISPLAY_NAME
column will be associated with the view with idandroid.R.id.text1
, etc.Note that you have passed a
null
cursor as the third argument to the constructor. This is very important, as we have not queried any data yet (this is theLoaderManager
andCursorLoader
's job). -
Next, initialize the loader.
getLoaderManager().initLoader(0, null, this);
This tells the
LoaderManager
to create and start theLoader
corresponding to id0
. -
The
LoaderManager
callsonCreateLoader(int id, Bundle args)
.onCreateloader
returns a subclass of theLoader<Cursor>
interface (i.e. aCursorLoader
, in this case). ThisCursorLoader
will perform the initial query and will update itself when the data changes.If your activity/fragment has more than one loader, then you'd use
switch(id)
to determine the specific loader that has been instructed to begin the loading process. -
The queried cursor is passed to
onLoadFinished()
.Immediately after the
CursorLoader
is instantiated and returned in step 3, theCursorLoader
performs the initial query on a separate thread and a cursor is returned. When theCursorLoader
finishes the query, it returns the newly queried cursor to theLoaderManager
, which then passes the cursor to theonLoadFinished
method. From the documentation, "theonLoadFinished
method is called when a previously created loader has finished its load." -
The queried data is associated with the
CursorAdapter
.mAdapter.swapCursor(data);
Note that
onLoadFinished
is also typically where you update the activity/fragment's UI with the queried data. This isn't necessary in this case, as we have previously calledsetListAdapter(mAdapter)
. TheListFragment
knows how to use theCursorAdapter
(see step 1)... all we need to do is pass the adapter the cursor withswapCursor
, and theListFragment
will take care of displaying the data on the screen for us.
Let me know if you have any questions (or if there are any typos, etc.).
TL;DR
The cursor that contains your queried data is associated with the CursorAdapter
in onLoadFinished
. This is typically done by calling mAdapter.swapCursor(data)
.
Related videos on Youtube
Andy
Love programming. Focused mainly on Android and Ruby on Rails full stack development. I also dabbles in the PHPs. SOreadytohelp
Updated on July 09, 2022Comments
-
Andy almost 2 years
I was going through some of my code and I realized I don't actually know how a
CursorLoader
andLoaderManager
combination used with aCursorAdapter
connect. Heres the part that I am confused in.agendaAdapter = new MyAgendaAdapter(this, null); makeProviderBundle(new String[] {"_id", "event_name", "start_date", "start_time", "end_date", "end_time", "location"}, "date(?) >= start_date and date(?) <= end_date", new String[]{getChosenDate(), getChosenDate()}, null); getLoaderManager().initLoader(0, myBundle, MainDisplayActivity.this); list.setAdapter(agendaAdapter);
So how does the
query()
method from my customContentProvider
know to send it to that specificCursorAdapter
? I just don't see the connection. I understand everything else in that but what this question is on. Oh and I should mention, the code works fine.-
Alex Lockwood almost 12 yearsI am beginning to suspect that you are asking these questions just to screw with me, knowing that I can't help but answer (even when I am at work lol)
-
Andy almost 12 yearsidk what you are talking about :) but if you're there...
-
-
SeanPONeil almost 12 yearsFantastic run through on loaders
-
Alex Lockwood almost 12 yearsDon't thank me... thank all of the crappy, poorly-developed apps on the Android market. They are the reason why I feel responsible for explaining this process in excessive detail. :)
-
Andy almost 12 yearsHey @AlexLockwood quick question. If I am using a CursorLoader with a LoaderManager I do not need to worry about closing the database or cursors right. But what about when I just use it directly like
getContentResolver().query()
. In that case do I have to explicitly close them, or does all that still get handled by the class itself? -
Alex Lockwood almost 12 yearsIf you're using a
CursorLoader
+LoaderManager
then you should not close theCursor
s (or else an exception will probably be thrown). The extremely short but detailed explanation of what happens is that theActivity
tells theLoaderManager
to perform certain actions during theActivity
lifecycle (i.e. it will instruct theLoaderManager
to retain itsLoader
s on a configuration change). TheLoaderManager
will react to these requests by calling theCursorLoader
methods (i.e.onStartLoading
,onStopLoading
, and a bunch of other methods inherited from theLoader
class). -
Alex Lockwood almost 12 yearsThen, in these methods the
CursorLoader
will close its cursors accordingly (i.e. inonReset
, etc.). In other words, it's not theLoaderManager
closing the cursors... it's theLoaderManager
telling theCursorLoader
to close the cursors. -
Alex Lockwood almost 12 yearsBut yeah... basically you never have to close the cursors when you are using
CursorLoader
s haha. You should close cursors queried withgetContentResolver().query
though, since raw cursors are not managed. -
IgorGanapolsky over 10 yearsWhere in the Activity/Fragment lifecycle is it best to call getLoaderManager().initLoader? I have looked at Google's sourcecode, and they do it all over the place: some in onCreate, onResume, onActivityCreated, onCreateView
-
Alex Lockwood over 10 years@IgorGanapolsky I remember discussing this with some other Android devs here. I always initialize loaders in
onActivityCreated()
. I agree with you that Google's documentation is a little vague about this point... but I have never had any issues with initializing Loaders inonActivityCreated()
. -
Cassio Landim almost 9 yearsI wanna have more control over my view. How can i replace
SimpleCursorAdapter
withCursorAdapter
usingCursorLoader
? -
f470071 over 8 yearsI would like to make something clear for me (and everyone else). After I swap the cursor with oldCursor = cursorAdapter.swapCursor(newCursor), should I close the oldCursor by myself or will that be taken care of or is that even a bad idea? I know that newCursor should not be closed under no circumstances at least unitil it is not swapped for a new one.