A number of colleagues have been singing the praises of Android
loaders since they were added in Honeycomb. I’ve pushed off learning loaders,
in part because I’m pretty comfortable with the main construct they replace:
the AsyncTask.

The other reason I think I shied away from
loaders is because the amount of documentation is overwhelming. Google offers a
partial example in its official documentation on loaders (the entire example can be downloaded from its SDK). Plus, there’s a very thorough
explanation and example on the Android Design Patterns blog. A tutorial
that must be divided into four parts is a pretty big commitment for most
Android coders who are already busy coding.

One nice thing about contributing to TechRepublic is now and
then I get to dive into things I’d ordinarily not have a reason to or make the
time for; this is especially true if it’s a topic that interests readers. After
scouring the web and reading lots about loaders, I’ve decided to write a
different kind of tutorial on the subject. Instead of being an
everything-you-ever-wanted-to-know about loaders guide, I like to think I’ve
pulled off a just-what-you-have-to-know-to-make-the-things-work tutorial.

What’s a loader,
and why would I use it?

Loaders aren’t trivial, so why use them in the first place? Well,
in most cases, you would use them in the same scenarios where you’ve been using
AsyncTasks; in fact, some loader subclasses extend AsyncTask. Just as AsyncTasks
are used to perform any long-running operation that would tie up the user
thread and ultimately throw the dreaded Application Not Responding (ANR), loaders
perform in the same manner with the same purpose. The main difference is loaders
are specialized for loading data. As such, loaders offer a number of efficiency
and convenience benefits.

The tutorial

Follow along with the step-by-step tutorial,
or, download and import the entire project directly into Eclipse.

1. Create a new Android project in Eclipse. Target Android
3.0 or higher (you can use loaders all the way back to Gingerbread if you
include the compatibility library.)

2. In the /res/layout folder, create you activity_main.xml
file. To keep things simple, I’ve implemented a default list view within a
linear layout.

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello Loaders!" />

<ListView
android:id="@+id/listview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

3. We’ll create our MainActivity.java file in the /src
directory. Let’s begin by extending a standard Android Activity and
implementing the LoaderCallbacks interface. The static int will eventually get
used to identify our loader.

MainActivity.java
package com.authorwjf.loaderexample;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends Activity implements LoaderCallbacks<List<String>>{

private static final int THE_LOADER = 0x01;
}

4. Let’s implement the required callbacks. The
onCreateLoader is responsible for building a loader, but the cool thing is, the
SDK takes care of hanging onto it. So the first time the method is called with
our 0x01 constant, a new loader is created. After that, the same instance is
returned. The onFinished callback returns with our data results, and the
onLoaderReset is responsible for wiping out the data.

@Override
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
SampleLoader loader = new SampleLoader(this);
return loader;
}

@Override
public void onLoadFinished(Loader<List<String>> loader, List<String> list) {
final ListView listview = (ListView) findViewById(R.id.listview);
final ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, list);
listview.setAdapter(adapter);
}

@Override
public void onLoaderReset(Loader<List<String>> loader) {
final ListView listview = (ListView) findViewById(R.id.listview);
listview.setAdapter(null);
}

5. Our onCreate function must initialize and kick off the loader
via the LoaderManager.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getLoaderManager().initLoader(THE_LOADER, null, this).forceLoad();
}

6. We are going to implement an inner static class that
extends AsyncTaskLoader. The loadInBackground override is provided for you to
load your actual data. For simplicity’s sake, I’m just iterating through a
hardcoded array of strings and introducing an artificial delay.

private static class SampleLoader extends AsyncTaskLoader<List<String>> {

public SampleLoader(Context context) {
super(context);
}

@Override
public List<String> loadInBackground() {

final String[] animals = new String[] { "Ape", "Bird", "Cat", "Dog", "Elephant","Fox",
"Gorilla", "Hyena", "Inch Worm", "Jackalope", "King Salmon","Lizard",
"Monkey", "Narwhal", "Octopus", "Pig", "Quail", "Rattle Snake", "Salamander",
"Tiger", "Urchin", "Vampire Bat", "Wombat", "X-Ray Tetra", "Yak", "Zebra"};

final ArrayList<String> list = new ArrayList<String>();
for (int i = 0; i < animals.length; ++i) {
list.add(animals[i]);
try {
Thread.sleep(100); //simulated network delay
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return list;
}

}

7. Build and load the APK to a device or emulator and give
it a whirl.

Final thoughts

If you ran the example, you probably said something to
yourself along the lines of, “Gee, that behaves pretty much like it would
have using an AsyncTask or even a Handler.” You’re right. But I think you’ll
also agree that there isn’t much more complexity than those other solutions
either.

Because this sample is so stripped down, it’s really little
more than a replacement for what you were probably using AsyncTask for. If you
feel comfortable with it, start exploring some of the more advanced examples of
loaders on the web, and you’ll begin to see benefits that far surpass what you
get out of the box with AsyncTasks and Handlers. The CursorLoader subclass might be a good next step,
especially if you’re doing a lot of work with SQLite.