This post introduces the Loader<D> class as well as custom Loader implementations. This is the third of a series of posts I will be writing on Loaders and the LoaderManager:
- Part 1: Life Before Loaders
- Part 2: Understanding the LoaderManager
- Part 3: Implementing Loaders
- Part 4: Tutorial: AppListLoader
First things first, if you haven’t read my previous two posts, I suggest you do so before continuing further. Here is a very brief summary of what this blog has covered so far. “Life Before Loaders” (part 1) described the flaws of the pre-Honeycomb 3.0 API and its tendency to perform lengthy queries on the main UI thread. These UI-unfriendly APIs resulted in unresponsive applications and were the primary motivation for introducing the Loader and the LoaderManager in Android 3.0. “Understanding the LoaderManager” (part 2) introduced the LoaderManager class and its role in delivering asynchronously loaded data to the client. The LoaderManager manages its Loaders across the Activity/Fragment lifecycle and can retain loaded data across trivial configuration changes.
Loader Basics
Loaders are responsible for performing queries on a separate thread, monitoring the data source for changes, and delivering new results to a registered listener (usually the LoaderManager) when changes are detected. These characteristics make Loaders a powerful addition to the Android SDK for several reasons:
They encapsulate the actual loading of data. The Activity/Fragment no longer needs to know how to load data. Instead, the Activity/Fragment delegates the task to the Loader, which carries out the request behind the scenes and has its results delivered back to the Activity/Fragment.
They abstract out the idea of threads from the client. The Activity/Fragment does not need to worry about offloading queries to a separate thread, as the Loader will do this automatically. This reduces code complexity and eliminates potential thread-related bugs.
They are entirely event-driven. Loaders monitor the underlying data source and automatically perform new loads for up-to-date results when changes are detected. This makes working with Loaders an absolute pleasure, as the client can simply trust that the Loader will auto-update its data on its own. All the Activity/Fragment has to do is initialize the Loader and respond to any results that might be delivered. Everything in between is done by the Loader.
Loaders are a somewhat advanced topic and may take some time getting used to. We begin by analyzing its four defining characteristics in the next section.
What Makes Up a Loader?
There are four characteristics which ultimately determine a Loader’s behavior:
A task to perform the asynchronous load. To ensure that loads are done on a separate thread, subclasses should extend
AsyncTaskLoader<D>as opposed to theLoader<D>class.AsyncTaskLoader<D>is an abstract Loader which provides anAsyncTaskto do its work. When subclassed, implementing the asynchronous task is as simple as implementing the abstractloadInBackground()method, which is called on a worker thread to perform the data load.A registered listener to receive the Loader’s results when it completes a load.1 For each of its Loaders, the LoaderManager registers an
OnLoadCompleteListener<D>which will forward the Loader’s delivered results to the client with a call toonLoadFinished(Loader<D> loader, D result). Loaders should deliver results to these registered listeners with a call toLoader#deliverResults(D result).One of three2 distinct states. Any given Loader will either be in a started, stopped, or reset state:
(a) Loaders in a started state execute loads and may deliver their results to the listener at any time. Started Loaders should monitor for changes and perform new loads when changes are detected. Once started, the Loader will remain in a started state until it is either stopped or reset. This is the only state in which
onLoadFinishedwill ever be called.(b) Loaders in a stopped state continue to monitor for changes but should not deliver results to the client. From a stopped state, the Loader may either be started or reset.
(c) Loaders in a reset state should not execute new loads, should not deliver new results, and should not monitor for changes. When a loader enters a reset state, it should invalidate and free any data associated with it for garbage collection (likewise, the client should make sure they remove any references to this data, since it will no longer be available). More often than not, reset Loaders will never be called again; however, in some cases they may be started, so they should be able to start running properly again if necessary.
An observer to receive notifications when the data source has changed. Loaders should implement an observer of some sort (i.e. a
ContentObserver, aBroadcastReceiver, etc.) to monitor the underlying data source for changes. When a change is detected, the observer should callLoader#onContentChanged(), which will either (a) force a new load if the Loader is in a started state or, (b) raise a flag indicating that a change has been made so that if the Loader is ever started again, it will know that it should reload its data.
By now you should have a basic understanding of how Loaders work. If not, I suggest you let it sink in for a bit and come back later to read through once more (reading the documentation never hurts either!). That being said, let’s get our hands dirty with the actual code!
Implementing the Loader
As I stated earlier, there is a lot that you must keep in mind when implementing your own custom Loaders. Subclasses must implement loadInBackground() and should override onStartLoading(), onStopLoading(), onReset(), onCanceled(), and deliverResults(D results) to achieve a fully functioning Loader. Overriding these methods is very important as the LoaderManager will call them regularly depending on the state of the Activity/Fragment lifecycle. For example, when an Activity is first started, the Activity instructs the LoaderManager to start each of its Loaders in Activity#onStart(). If a Loader is not already started, the LoaderManager calls startLoading(), which puts the Loader in a started state and immediately calls the Loader’s onStartLoading() method. In other words, a lot of work that the LoaderManager does behind the scenes relies on the Loader being correctly implemented, so don’t take the task of implementing these methods lightly!
The code below serves as a template of what a Loader implementation typically looks like. The SampleLoader queries a list of SampleItem objects and delivers a List<SampleItem> to the client:
public SampleLoader extends AsyncTaskLoader<List<SampleItem>> {
// We hold a reference to the Loader’s data here.
private List<SampleItem> mData;
public SampleLoader(Context ctx) {
// Loaders may be used across multiple Activitys (assuming they aren't
// bound to the LoaderManager), so NEVER hold a reference to the context
// directly. Doing so will cause you to leak an entire Activity's context.
// The superclass constructor will store a reference to the Application
// Context instead, and can be retrieved with a call to getContext().
super(ctx);
}
/****************************************************/
/** (1) A task that performs the asynchronous load **/
/****************************************************/
@Override
public List<SampleItem> loadInBackground() {
// This method is called on a background thread and should generate a
// new set of data to be delivered back to the client.
List<SampleItem> data = new ArrayList<SampleItem>();
// TODO: Perform the query here and add the results to 'data'.
return data;
}
/********************************************************/
/** (2) Deliver the results to the registered listener **/
/********************************************************/
@Override
public void deliverResult(List<SampleItem> data) {
if (isReset()) {
// The Loader has been reset; ignore the result and invalidate the data.
releaseResources(data);
return;
}
// Hold a reference to the old data so it doesn't get garbage collected.
// We must protect it until the new data has been delivered.
List<SampleItem> oldData = mData;
mData = data;
if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
}
/*********************************************************/
/** (3) Implement the Loader’s state-dependent behavior **/
/*********************************************************/
@Override
protected void onStartLoading() {
if (mData != null) {
// Deliver any previously loaded data immediately.
deliverResult(mData);
}
// Begin monitoring the underlying data source.
if (mObserver == null) {
mObserver = new SampleObserver();
// TODO: register the observer
}
if (takeContentChanged() || mData == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
@Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
// Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
}
@Override
protected void onReset() {
// Ensure the loader has been stopped.
onStopLoading();
// At this point we can release the resources associated with 'mData'.
if (mData != null) {
releaseResources(mData);
mData = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
if (mObserver != null) {
// TODO: unregister the observer
mObserver = null;
}
}
@Override
public void onCanceled(List<SampleItem> data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
// The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
}
private void releaseResources(List<SampleItem> data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
}
/*********************************************************************/
/** (4) Observer which receives notifications when the data changes **/
/*********************************************************************/
// NOTE: Implementing an observer is outside the scope of this post (this example
// uses a made-up "SampleObserver" to illustrate when/where the observer should
// be initialized).
// The observer could be anything so long as it is able to detect content changes
// and report them to the loader with a call to onContentChanged(). For example,
// if you were writing a Loader which loads a list of all installed applications
// on the device, the observer could be a BroadcastReceiver that listens for the
// ACTION_PACKAGE_ADDED intent, and calls onContentChanged() on the particular
// Loader whenever the receiver detects that a new application has been installed.
// Please don’t hesitate to leave a comment if you still find this confusing! :)
private SampleObserver mObserver;
}
Conclusion
I hope these posts were useful and gave you a better understanding of how Loaders and the LoaderManager work together to perform asynchronous, auto-updating queries. Remember that Loaders are your friends... if you use them, your app will benefit in both responsiveness and the amount of code you need to write to get everything working properly! Hopefully I could help lessen the learning curve a bit by detailing them out!
As always, please don’t hesitate to leave a comment if you have any questions! And don't forget to +1 this blog in the top right corner if you found it helpful! :)
1 You don't need to worry about registering a listener for your Loader unless you plan on using it without the LoaderManager. The LoaderManager will act as this "listener" and will forward any results that the Loader delivers to the
LoaderCallbacks#onLoadFinished method.2 Loaders may also be in an “abandoned” state. This is an optional intermediary state between “stopped” and “reset” and is not discussed here for the sake of brevity. That said, in my experience implementing
onAbandon() is usually not necessary.

Thanks a lot for the post. I think it is very interesting idea to make custom loader for making network requests. In that way client will be abstracted where data is gathered from: database, network, local cache or something else.
ReplyDeleteHi Alex how would you recommend getting data from the observer to the loader?
ReplyDeleteFor example, what if I recieved a location change within a BroadcastReceiver that was set to listen for location updates. How would I get that latest location into the loader, so as the loader know's what is the most latest data to work on?
Thanks in advance and thank you for the blog posts!
@Sent1nel Thanks for the comment!
ReplyDeleteThe observer is only there to listen for content changes. In other words, it will never "send" data to the loader... rather, it will notify the loader when a change has occurred, at which point the Loader should respond to this notification by performing a new query and delivering the up-to-date results. The observer will usually hold a private reference to the Loader so that it may call onContentChanged() on it when a change has been made (see the above post for more info on onContentChanged()).
A good example can be found in the AsyncTaskLoader sample code (scroll down until you find the PackageIntentReceiver).
@Sent1nel
ReplyDeleteIn your example, when you get data from the observer(in your case, a BroadcastReceiver), you should not pass the data directly to the Loader class, because doing this will change the pre-assumed behavior of what a Loader should, thus make users of Loader(like a LoaderManager) confused, or even wrongly functioned.
However, it's fine to get data from the observer, you should store the data somewhere, and then ask the Loader to "load it"(That's what a Loader should do, right?). The "somewhere" could be a file, a table in the database, or even simply a private member of the Loader class.
@Alex Lockwood
ReplyDeleteThe onStartLoading() method is a little bit confusing here. Is it called before or after loadInBackground() is executed? Or is it even called in the same thread as loadInBackground()?
In any situation(before or after or in different thread), does your template's call to forceLoader() make the loadInBackground() method executed twice?
Carl,
ReplyDeleteonStartLoading() is called when the Loader is first started (the LoaderManager starts each Loader by calling Loader#startLoading(), which immediately calls onStartLoading()), so it is one of the very first methods that gets called. onStartLoading() will always be called on the main UI thread. Further, forceLoad() is the only method in the sample code above that will cause loadInBackground() to be called, so it will always be called after onStartLoading().
Hope that cleared things up!
When onStopLoading() and onReset is called?
ReplyDeleteI debug the custom AsyncTask Loader code which is provided with API Demos to list already installed application. If I press the back button after the 1st launch, only onStopLoading()method is called.
In case CursorLoader when I press back button LoaderManager's onLoaderReset() method is called.
In case of custom AsyncTask Loader when onLoadReset() method is called and why onLoadReset()method is not call by pressing back?
onStopLoading and onReset are called by the LoaderManager during Activity/Fragment lifecycle events. I'm not sure why you are experiencing behavior you are describing... it's too difficult to tell without seeing the code.
ReplyDeleteEither way, you probably don't need to worry about it though. The LoaderManager will call onLoaderReset when it decides it is necessary to do so (i.e. it is smart enough to determine whether or not it has delivered new data that must be reset... if new data has never been delivered, it won't waste time calling the Loader's onReset method). As long as your Loader is implemented correctly (as described above), you should be fine.
http://developer.android.com/reference/android/content/AsyncTaskLoader.html
ReplyDeletefor LoaderCursor
http://developer.android.com/guide/components/loaders.html
I am rererring these code only. Sorry!!! By pressing "back" in case of AsyncTaskLoader nothing is happening. But by pressing back in case of CursorLoader LoaderManager's onLoaderReset() method is called.
I have checked that when you press Home Button in AsyncTask Loader only onStopLoading() method is called. If possible please clarify the Back Button press event for AsyncTaskLoader. According to me LoaderManager's callback methods should behave same for both of them.
See this line of code. The LoaderManager will only call onLoaderReset if (1) the callbacks are not null, (2) the loader is not null, (3) the loader has data, (4) and the Loader has successfully delivered this data to the client. If onLoaderReset is not being called, then at least one of these conditions has not been satisfied.
ReplyDeleteYou can try and figure out which one of those conditions is not satisfied if you really want, but the fact remains that it is NOT any of your concern. The LoaderManager does its job very well... it knows when it should call onLoaderReset. In other words, the AppListLoader's onLoadReset method is not called when you press the back button because the LoaderManager has decided it is not necessary. This is not a bug on your end. :)
When mData exists, onStartload will call deliverResult with mData as param. At this situation, it seems that mData, data, oldData in deliverResult method are one same object, and in onReleaseResources it will release the data that are using.
ReplyDeleteAre you using the APIDemos code? Because if you are, there is a very subtle bug that might be causing this problem. See lines 267-268 and change:
ReplyDeleteList oldApps = apps;
mApps = apps;
to
List oldApps = mApps;
mApps = apps;
If this is not the problem, feel free to shoot me an email with your code... I'd be interested to see it!
In deliverResult() method, why do we need to check for states. I thought this should be handle in the LoaderManager? E.g. LoaderManager should have onReleaseResources(D) and not call deliverResult() when Loader is in reset state in the first place?
ReplyDelete"In deliverResult() method, why do we need to check for states?"
ReplyDeleteThe onReset() in deliverResults(D data) covers the case in which an asynchronous query has completed, but the Loader has since been reset. What probably happened was: (1) a started Loader began a query on a background thread, (2) while the background thread was executing in the background, the Loader was stopped and reset, (3) when the background query completed and loadInBackground() returned the new data, the AsyncTaskLoader immediately delivered it to the client without checking to see if the Loader was reset. Therefore we must check to see if this is the case in deliverResults(D data), freeing any resources associated with the data if necessary.
It seems to me that it would have been a better idea to add these checks in the AsyncTaskLoader implementation... then again, I suppose the Android team must have had a good reason for doing otherwise. :)
Actually I mean the code show above has this problem.
ReplyDeleteLine 68 call deliverResult(mData),so in deliverResult method data, mData, oldData are same, and will release the data in use.
Oops, you're right! I believe changing line 55 from "if (oldData != null)" to "if (oldData != null && oldData != data)" should do the trick. Thanks for the catch! :)
ReplyDeleteOn Configuration change, onStartLoading() is not called , but old results are delivered. How to force re-query on configuration change ?
ReplyDeletenever mind, restartLoader().forceLoad() forces requery.
ReplyDeleteCHEERS MATE
ReplyDeleteWell, you wrote the best tutorials I found about this new way of implementing database driven applications. I still think is too complicated for me as I have just started learning Android. I will go the old way, using a singleton as my SQLiteOpenHelper and all that stuff. Even Google did not update their own Notepad tutorial...lol...
ReplyDeleteHey Mirko, thanks for reading. If you don't want to use a ContentProvider but still would like to use a Loader, you might want to check out Mark Murphy's LoaderEx project. Writing an entire ContentProvider is kind of overkill if all you want to do is use a Loader (if that is what you mean)... Mark's SQLiteCursorLoader makes it possible to load data asynchronously this way.
ReplyDeleteAlex,
ReplyDeleteI'm notsure if I am being obtuse or just a bit slow on the uptake but, nowhere can I find a Loader explicitly returning a Cursor object.
I make this point because I want to create a custom CursorAdapter for the ListView, which of course needs a Cursor object as aparameter.
Can I just cast a CursorLoader to a Cursor or even use a CursorLoader with CursorAdapter ?
Thanks, Mike
Hi Alex .. Thank you so much for the Loader Tutorials .. you have demystified the concept of loaders via your tutorials , I now understand how powerful loaders are . Thank you again and keep on writing the good stuff .. Cheers :D
ReplyDeleteThanks for the comment, Christpher! Glad I could help!
DeleteHi Alex, thank you so much for these multi-part tutorials. I was wondering if you could show a quick code snippet on how to call the custom loader? I looked at your next tutorial which is a great version of the ApiDemo, but there were so much noise (sorry, no pun intended) that I could not really understand how the custom loader is invoked....happy new year by the way....HT
ReplyDeleteIt's exactly the same as how you would use a CursorLoader in your application. Loaders are self-contained... their internals are completely hidden from the application. So even though the internals seem very "noisy", as you put it, all Loaders are used in the exact same way from the Activity/Fragment's point of view (see MainActivity.java). Hope that makes sense!
DeleteDear Alex, thanks again so much for this. After mulling the whole new year's day on this code, i finally figured out enough of the code flow to make both the CursorLoader and custom loader to work on a simple sqlite database read via my own content provider. I have a question for you if you dont mind: since both loaders appear to share many of the methods (particularly onCreateLoader and onLoadFinished, how would a program implement both types of loaders at once, especially if the custom loader does not return the same data type? For example, currently, my code specifies both loaders return a Cursor, but what if my custom loader is meant to return a different type? Again, thoroughly enjoyed your post....HT
DeleteIf you have two Loaders that return results with different types, you would need to instantiate two separate instances of the LoaderManager.LoaderCallbacks<D> interface and then feed each to the LoaderManager individually. So something like,
Delete// Callbacks for the first Loader
LoaderManager.LoaderCallbacks<Cursor> callBacks1 = new LoaderManager.LoaderCallbacks<Cursor>() {
/* Implement the three callback methods here */
};
getLoaderManager().initLoader(0, null, callBacks1);
// Callbacks for the second Loader
LoaderManager.LoaderCallbacks<List<String>> callBacks2 = new LoaderManager.LoaderCallbacks<List<String>>() {
/* Implement the three callback methods here */
};
getLoaderManager().initLoader(1, null, callBacks2);
There might be some typos but you get the idea. :)
Hey Alex, you're right on the above, i got the two loaders to work now. For your interest, the reason i originally could not make the concept working was two-fold:
Delete1) My UI activity class implemented one LoaderCallbacks, so it would not let me do a different set of call back methods as you suggested. Once I removed that Implement, I could now go ahead use your suggested code construct
2) part of the problem, may be you have discussed here and i missed, is that the AsyncTaskLoader would not start unless you force it. I found this from another post. The snippet is something like below, I added into my class and it started working.
protected void onStartLoading() {
super.onStartLoading();
// AsyncTaskLoader doesn't start unless you forceLoad -http://code.google.com/p/android/issues/detail?id=14944
if(myCursor != null){
deliverResult(myCursor);
}
if(takeContentChanged() || myCursor == null){
forceLoad();
}
}
Thanks Again! :)
Yeah, that link isn't a bug... it's an inconvenience at best. It just means that you can't forget to call forceLoad() in your onStartLoading() implementation. Glad to see you figured it out. :)
DeleteHey Alex, thanks for the great writeup. I really learned a lot. In the "What makes up a loader" section, for the many apps that use loaders that fetch data from the internet, #4 doesn't seem like it would be possible. Would you recommend any best practices for constructing those types of loaders?
ReplyDeleteNo, I wouldn't recommend it. If you do it that way, you lose the ability to cache previously fetched data and your app will not be able to work offline. I would recommend instead creating a background Service that polls data from the server every once in a while and inserts new data into a ContentProvider. Then just use a CursorLoader to load the data from the CP instead. In other words, the Loader/Activity have no clue that web requests are actually being performed in the background... from their perspective they are just simply querying data from a local CP. It's up to the Service to poll for new data and insert it into the CP when new data is downloaded.
DeleteIf you want to avoid polling for data all together, read up on Google Cloud Messaging. It requires more work to setup but it's totally worth it because your app will no longer have to constantly poll for new data every 5-10 minutes or so.
Damn, doing it the "right way" is a lot of work.
DeleteIt's worth it. And you can learn some Google App Engine while you're at it. :)
DeleteHello Alex, and congrats for your post. It's very clear and useful.
DeleteI also like doing the right way but, what if want to get data from a server and I know that the data will not change in long term? I could concrete on states and cities sets of data. I mean, for example, I want to populate 2 spinners (States and Cities). The states spinner will be filled with states names and the cities spinner will be filled with cities from the selected state in the first spinner. Questions are:
- May I implement CP for both entities (State & City) and implement a service to populate 2 SQLite tables to cache the data?
- If not, let's assume that I implement an AsynTask to retrieve data from server, but a third spinner has 5 o 6 choices that come from user preferences (for example, favourite state and city to fast fill state and city spinners). I'd implement a CursorLoader to fill this spinner, but, how do I combine Loader with AsyncTask (for network data retrive)?
Using a Loader to perform network requests isn't great practice, because (1) it means that your application will be hard on the battery (having to poll for new data from the network repeatedly each time you start the Activity, (2) there is no way to observer the network for content changes without polling it repeatedly, and (3) your application won't work offline.
DeleteSo my answer is to forget about using the Loader/AsyncTask combination entirely and to stick with a Service. The Service can poll the network for data every once and a while and insert new data into a ContentProvider. You can then use a CursorLoader to load data from the ContentProvider without it needing to know anything about where the data coming from.
Hello Alex,
ReplyDelete(1) "Loaders may be used across multiple Activitys (assuming they aren't bound to the LoaderManager), so NEVER hold a reference to the context directly. Doing so will cause you to leak an entire Activity's context."
(2) "The superclass constructor will store a reference to the Application Context instead, and can be retrieved with a call to getContext()."
If storing the context is bad for us, why does this not also apply for the superclass?
public Loader(Context context) {
mContext = context.getApplicationContext();
}
Thank you.
Cristian
Because each application can have many Activitys, and it is possible to leak them if you aren't careful. Storing the application context doesn't result in the possibility of a memory leak because there is only one in the during the entire application lifecycle and it will always exist. Referencing the application context instead of an Activity context is usually a good way to avoid leaking an entire context in this way.
DeleteThank you so much Alex
DeleteHi Alex thanks for this tutorial. I would like to know if you have tried using a custom CursorLoader. I know that CursorLoader should be used with content providers but after reading the post in https://groups.google.com/forum/#!topic/android-developers/J-Uql3Mn73Y, I thought it is possible to just use the CursorLoader and modify it to query the database as recommended by Dianne Hackborn. So what I did was this:
ReplyDeletepublic class MyLoader extends CursorLoader {
public MyLoader(Context context) {
super(context)
}
@Override
public Cursor loadInBackground() {
return cursor = new MyDB().getCursorFromDb();
}
}
This works correctly when I call the getLoaderManager().getLoader(0).onContentChange() everytime I receive changes in the database. I am not sure though if this practice is correct or it will cause problems later on since I am calling onContentChange() explicitly.
I hope you could shed some light into this. thanks very much.
Dianne Hackborn isn't suggesting you extend the CursorLoader... she is suggesting that you "use its code and replace the one line where it queries for the cursor." I wouldn't extend the CursorLoader directly (if you do this, your result will still be a CursorLoader and that is kind of messy)... instead I would implement your own Loader using the CursorLoader source code as a guide. Like Dianne said, you will not have to modify much... probably just that single line where you query the Cursor.
DeleteYou can also use Mark Murphy's LoaderEx library too... he has implemented an "SQLiteLoader" that does exactly what you need.
Thanks for the insight!
DeleteWell done sir!
ReplyDeleteExpert instructions! Thanks a lot.
ReplyDelete