Software Development

Android loaders: We meet at last

William J. Francis gives you a tutorial on Android loaders that covers just what you need to know to make things work.

android_091913.png
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.

hello_android_loaders2_102513.png

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.

About

William J Francis began programming computers at age eleven. Specializing in embedded and mobile platforms, he has more than 20 years of professional software engineering under his belt, including a four year stint in the US Army's Military Intellige...

5 comments
jedabero
jedabero

efffingthank you man!!! srsly finally I  understand this thing about loaders! THANK YOU! a million thanks!

eazyigz
eazyigz

Nice tutorial, but it would be good if you demonstrated LoaderManager.LoaderCallbacks with a cursor from db, instead of just a simple list.

chanakyavikas
chanakyavikas

Thanks for the tutorial. But can't you use the code that you have used in

"loadInBackground()" directly inside "onCreateLoader(int id, Bundle args) " method.

chanakyavikas
chanakyavikas

Thanks for the tutorial. But can't I use the code that you have used in

"loadInBackground()" directly inside "onCreateLoader(int id, Bundle args) " method.

chanakyavikas
chanakyavikas

Thanks for the tutorial. But Can't you use the code what you have used in "loadInBackground()" directly inside "onCreateLoader()" method. 

Editor's Picks