Advanced

[TUT] Networking off the UI thread – SOLID Architecture

Hi guys, this is a Tutorial Request to show how to architect an app in a S.O.L.I.D design to complete networking tasks.

Now I am not an expert at this, but I believe the example does show the signs of a SOLID design. I can also see places where I could have used more interfaces and perhaps reduced responsibility. The one thing I noticed is, it is hard to design an application without some end goal to complete. What I mean is, having a scenario to code to I find is much easier than trying to make something generic that most people can grasp and mould to their needs. Yes I am actually saying I need requirements. With that said lets crack on!

In this tutorial we are going to create an application that runs a service spawning background threads. These background threads can run tasks that talk to the internet / parse output / perform complex tasks. Once these are complete they can send a response back to the calling Activity allowing it to display data, update UI components or just acknowledge the task is complete.

This will involve:

  • Creating singular tasks that can be run on a separate thread (internet stuff)
  • Create a service that will run and manage these threads
  • Have an interface so each activity can request a unit of work to be done
  • Create a ‘super’ activity so all task code is in one place
  • Have an Activity example that can call the tasks and receive the outcome

Ok .. here … we .. go.

Please check the source code for the full compilable project, it also heavily commented (which isn’t recommended in a SOLID design).
This code is a bit circular so bear with it.

First off we need to create our tasks, these implement runnable signalling they are capable of running as a single unit of work. We will create a hierarchy of Tasks, each task will implement from an abstract task class. This allows us to put the communication between the task and service in once place and the actual task just does it work then says “I’ve finished”.

Each task has a Message class field, this is used to hold any data the task wishes to send back, it also holds a reference to where the task came from (i.e. our service).

android.os.Message.java

Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.

While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.

Here is the super class for each Task:

Task.java

package com.blundell.solidnetworking.service.factory;

import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;

/**
 * A single unit of work that is executed as a task
 * @author paul.blundell
 *
 */
public abstract class Task implements Runnable {

	protected final Message msg;

	public Task(Message msg) {
		this.msg = Message.obtain(msg);
	}

	@Override
	public abstract void run();

	/**
	 * Call this if you want to add data to your message before it is sent back up to your Activity
	 * @param data attach any data to the bundle here
	 */
	protected void setResponseData(Bundle data){
		msg.setData(data);
	}

	/**
	 * Replies to our Service saying this task is completed
	 * @param response The response message to send
	 */
	protected void sendReply() {
		Messenger replyTo = msg.replyTo;
		if (replyTo != null) {
			try {
				replyTo.send(msg);
			} catch (Exception e) { // Mostly RemoteException
				try {
					msg.what = 666; // Some error code
					replyTo.send(msg);
				} catch (RemoteException e1) { /** Nothing we can do */ }
			}
		}
	}
}

An example implementation is the DoSomethingTask below. As you can see it is possible to give each task some input and send back output. In the DoSomethingTask we just send a String and send back another String, you can use more complex objects just make them implement serializable. Anything you can send in a Bundle can be sent and received by the Task.

DoSomethingTask.java

package com.blundell.solidnetworking.service.factory.task;

import com.blundell.solidnetworking.service.factory.Task;
import com.blundell.solidnetworking.util.Log;

import android.os.Bundle;
import android.os.Message;

/**
 * @author paul.blundell
 * Here you can do networking or other long running 'tasks' that do not belong on the main thread
 */
public class DoSomethingTask extends Task {
	public static final String SOME_STRING = "someString";
	public static final String INPUT_VAR = "someInputVar";

	public DoSomethingTask(Message msg) {
		super(msg);
	}

	@Override
	public void run() {
		// Do some work
		Log.i("Hi, I'm on another thread");

		Log.i("I received the variable: " + msg.getData().getString(INPUT_VAR));

		// Set the response - according to the work done
		Bundle b = new Bundle();
		b.putString(SOME_STRING, "test string in bundle, created in task - sent back to activity (responselistener)");
		setResponseData(b);

		// Finished so send reply
		sendReply();
	}

}

Now we have our tasks setup we need someway of calling them from our Activity. However just having the tasks implement Runnable does not make them run on there own thread. We need a TaskExecutor, android has a nice one right here (ThreadPoolExecutor). I recommend reading the API documentation on that class. Alright we can’t just dump a ThreadPoolExecutor in ever Activity thats just nasty. So we wrap the executor in an Android Service, that way we can bind to it from each Activity and get our tasks running yay!

SolidService.java

package com.blundell.solidnetworking.service;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.blundell.solidnetworking.service.factory.Task;
import com.blundell.solidnetworking.service.factory.TaskFactory;
import com.blundell.solidnetworking.util.Log;

import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Messenger;

/**
 * Our service is used to manage the running of tasks. We have a queue of tasks that need to be ran
 * currently this runs the tasks in a linear fashion. Therefore tasks that send replys to the UI could be queued behind fire and forget tasks *hint (no comparison)*
 * @author paul.blundell
 *
 */
public class SolidService extends Service {
	private static final int QUEUE_CAPACITY = 10;
	// A handler for incoming messages from our client
	private Messenger messenger;
	// A pool of threads for the android system to manage our tasks
	private static ThreadPoolExecutor executor;
	// A factory to help us retrieve our tasks
	private static TaskFactory factory;

	@Override
	public void onCreate() {
		super.onCreate();
		messenger = new Messenger(messageHandler);
		executor = new ThreadPoolExecutor(1, 3, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(QUEUE_CAPACITY));
		factory = new TaskFactory();
	}

	@Override
	public IBinder onBind(Intent intent) {
		Log.d("Service was bound");
		return messenger.getBinder();
	}

	protected static Handler messageHandler = new Handler() {
		@Override
		public void handleMessage(android.os.Message msg) {
			Task task = factory.getTask(msg);
			if(task != null)
				executor.execute(task);
		};
	};

	@Override
	public void onDestroy() {
		waitForTasksToFinishThenStop();
		messenger = null;
		factory = null;
		super.onDestroy();
	}

	private static void waitForTasksToFinishThenStop() {
		executor.shutdown();
		try {
			executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
		} catch (InterruptedException e) {
			Log.e("Unfinished tasks", e);
		}
		executor = null;
	}
}

It’s important to notice how the service shutsdown the executor when it is finishing. This stops nasty bugs with connections being open, callbacks when you don’t expect it or even open databases ew. We also use a TaskFactory here so that we can easily add new tasks as necessary.

TaskFactory.java

package com.blundell.solidnetworking.service.factory;

import com.blundell.solidnetworking.R;
import com.blundell.solidnetworking.service.factory.task.DoSomethingTask;
import com.blundell.solidnetworking.service.factory.task.FireAndForgetTask;
import com.blundell.solidnetworking.util.Log;

import android.os.Message;

/**
 * This is used by our service to return the appropriate task that was asked for
 * @author paul.blundell
 *
 */
public class TaskFactory {

	public Task getTask(Message msg) {
		switch (msg.what) {
		case R.id.task_do_something:
			return new DoSomethingTask(msg);
		case R.id.task_do_fire_and_forget:
			return new FireAndForgetTask(msg);
		default:
			Log.d("No task found");
			return null;
		}
	}

}

Now we need to bind to the service to get it started. This is done using a ServiceConnectionManager, it does what it says on the tin, manages the connection to the service. We talk to the service using Messages and in the connection manager we hold a queue sending messages as they are received from the Activity’s (i.e. as we request to do things on the internet).

ServiceConnectionManager.java

package com.blundell.solidnetworking.service;

import java.lang.ref.WeakReference;
import java.util.LinkedList;

import com.blundell.solidnetworking.util.Log;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.*;

/**
 * We deal with calls to the service in a FIFO manner
 *
 * @author Blundell
 */
public abstract class ServiceConnectionManager implements ServiceConnection {
	// The context the service is running in
	private Context context;
	// If the service has been bound yet or not
	private boolean bound;
	// A handler to the service to send messages
	private Messenger service;
	// A queue of tasks that the app is wanting the service to do
	private final LinkedList<Message> msgQueue;
	// This is the listener if the Activity wants to get a response from the task ran by the service
	private final ReplyHandler replyTo;

	public ServiceConnectionManager(Context context, ResponseListener listener) {
		this.context = context;
		replyTo = new ReplyHandler(listener);
		msgQueue = new LinkedList<Message>();
		context.bindService(new Intent(context, SolidService.class), this, Context.BIND_AUTO_CREATE);
	}

	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		this.service = new Messenger(service);
        bound = true;

        // If there is a msg waiting to be sent, send after binding
        sendServiceMessage();
	}

	/**
	 * Get a message with the id of the task we want to run
	 * @param taskId
	 * @return
	 */
	protected Message obtainMessage(int taskId){
		Message msg = Message.obtain();
		msg.replyTo = new Messenger(replyTo);
		msg.what 	= taskId;
		return msg;
	}

	protected void addToQueue(Message msg) {
		msgQueue.add(msg);
		if(bound) {
			sendServiceMessage();
		} else {
			Log.d("The service is not bound.");
        	Log.d("Message ID was:" + Log.identifyMessage(context.getResources(), msg));
        	Log.d("Message was added to the queue and service call will be done on next call (when we're bound)");
		}
	}

	private void sendServiceMessage() {
		try {
			while(!msgQueue.isEmpty()){
				Message msg = msgQueue.removeFirst();
				service.send(msg);
				Log.d("Sent the service the msg: "+Log.identifyMessage(context.getResources(), msg));
			}
		} catch (RemoteException e) {
			Log.d("Can't do much about remote exceptions.");
		}
	}

	private static class ReplyHandler extends Handler {
		private final WeakReference<ResponseListener> mListener;

		public ReplyHandler(ResponseListener listener) {
			mListener = new WeakReference<ResponseListener>(listener);
		}

		@Override
		public void handleMessage(Message msg) {
			// We now pass the message onto the Activity if it set a listener (which we are enforcing it does with the BlundellActivity, using the ResponseListener interface)
			if(mListener.get() != null)
				mListener.get().handleServiceResponse(msg);
		}
	}

	@Override
	public void onServiceDisconnected(ComponentName name) {
		service = null;
        bound = false;
	}

	public void stop(){
		msgQueue.clear();
		context.unbindService(this);
		bound = false;
		context = null;
	}
}

Attempting to ensure the single responsibility principle we have a secondary class called the ServiceClient this is where the Activity talks to the service, it holds a nice interface so the Activity has no idea how we implement the service, it then calls the ServiceConnectionManager adding to its queue.

If you wanted to create a new task, this is where you would start, create an identifier for your task, create a new message and send it to the service. Then create your task and add it to the TaskFactory. Simple?

ServiceClient.java

package com.blundell.solidnetworking.service;

import com.blundell.solidnetworking.R;
import com.blundell.solidnetworking.service.factory.task.DoSomethingTask;

import android.content.Context;
import android.os.Message;

/**
 * A client for our service, here we create the 'interface' that our activity's can talk to.
 * @author paul.blundell
 *
 */
public class ServiceClient extends ServiceConnectionManager {

	public ServiceClient(Context context, ResponseListener listener) {
		super(context, listener);
	}

	public void doSomething(String input){
		Message msg = obtainMessage(R.id.task_do_something);
		msg.getData().putString(DoSomethingTask.INPUT_VAR, input);
		addToQueue(msg);
	}

	public void doFireAndForgetTask() {
		Message msg = obtainMessage(R.id.task_do_fire_and_forget);
		addToQueue(msg);
	}

}

Alright sorry for going backwards but recall the ServiceConnectionManager, this needs to send messages back to the activities when the tasks are complete. We do this using an interface called ResponseListener. You could implement this on any object and it will receive callbacks when your task has completed.

ResponseListener.java

package com.blundell.solidnetworking.service;

import android.os.Message;

/**
 *
 * @author paul.blundell
 *
 * This is the interface stating we are listening to our tasks for a reply
 *
 */
public interface ResponseListener {

	public abstract void handleServiceResponse(Message msg);

}

Thats it you could create an Activity to use this architecture, however we are going to take it a step further and use a super activity to handle connecting and talking to our service. This means each activity will bind to the service and allow it to call our ServiceClient asking for tasks to be complete. I would always advice creating a super activity. It is handy for grouping duplicate code, obviously you won’t have any code when you create a new application but better off putting it in now than having to retro fit it, you will need it.

BlundellActivity.java

package com.blundell.solidnetworking.ui;

import com.blundell.solidnetworking.service.ResponseListener;
import com.blundell.solidnetworking.service.ServiceClient;
import com.blundell.solidnetworking.util.Log;

import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.widget.Toast;

/**
 * Our 'super' activity, here we can put common code. It is also used to bind to our service,
 * therefore any class that subclasses this class can talk to the service and perform tasks off the UI thread.
 * @author paul.blundell
 *
 */
public abstract class BlundellActivity extends Activity implements ResponseListener {

	private ServiceClient serviceClient;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		lazyInitService();
	}

	@Override
	protected void onResume() {
		super.onResume();
		lazyInitService();
	}

    protected ServiceClient getServiceClient() {
    	lazyInitService();
        Log.d("Returning service from super activity");
        return serviceClient;
    }

	private void lazyInitService() {
		if(serviceClient == null) {
			Log.d("Created new service");
			serviceClient = new ServiceClient(this, this);
		}
	}

	public void popToast(String msg){
		Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
	}

	public void popBurntToast(String msg){
		Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
	}

	/** Does nothing here */
	@Override
	public void handleServiceResponse(Message msg){
		// Implemented in sub classes
		// If you wanted you could check for errors here and shown a generic dialog error message
	}

	@Override
	protected void onStop() {
		stopService();
		super.onStop();
	}

	private void stopService() {
		if(serviceClient != null){
			Log.d("super activity onStop - stopping service");
			serviceClient.stop();
			serviceClient = null;
		}
	}
}

Finally here is an example activity that runs the tasks. Don’t forget you are binding to a service in the super onCreate, so any task request in the onCreate will be slightly delayed (because it will be added to the queue but not ran until the service is bound).
This example doesn’t do much, but you can imagine doing a network task, parsing the result and sending back some object to your Activity. When this activity receives the reply you can update your UI, perhaps adding text or filling a list. I do have examples of such tasks in some of my other tutorials (which use a similar architecture) so take a look around.

MainActivity.java

package com.blundell.solidnetworking.ui.phone;

import com.blundell.solidnetworking.FromXML;
import com.blundell.solidnetworking.R;
import com.blundell.solidnetworking.service.factory.task.DoSomethingTask;
import com.blundell.solidnetworking.ui.BlundellActivity;
import com.blundell.solidnetworking.util.Log;

import android.os.Bundle;
import android.os.Message;
import android.view.View;

/**
 *
 * @author paul.blundell
 *
 */
public class MainActivity extends BlundellActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @FromXML
    public void doFireAndForgetTask(View button){
    	getServiceClient().doFireAndForgetTask();
    }

    @FromXML
    public void doSomethingTask(View button){
    	getServiceClient().doSomething("I sent this");
    }

    @Override
    public void handleServiceResponse(Message msg) {
    	dealWithResponse(msg.what, msg.getData());
    }

	private void dealWithResponse(int responseId, Bundle data) {
		switch(responseId){
    	case R.id.task_do_something:
			dealWithDoSomethingResult(data);
    		break;
    	default:
    		Log.v("Received service reply that wasn't handled");
    	}
	}

	private void dealWithDoSomethingResult(Bundle data) {
		String string = data.getString(DoSomethingTask.SOME_STRING);

		Log.d("Received reply from the task. The var was: "+ string);

		popToast("Do Something Task Complete");
	}
}

I hope you’ve find this useful, please comment with any questions. One thing I would say is don’t take this as the only way to do this. There are many architectures you could use, also using other Android framework classes like Loaders or ASyncTasks. This architecture can also be improved by adding comparability to the tasks, then you could give priority to your UI changing tasks and de-prioritize menial tasks like analytics etc.

Source Code:

Solid Networking Tut Eclipse Source

Git Hub Repo – SOLIDNetworkingTut

[TUT] ASyncTaskLoader using Support Library

I’ve just started learning about Loaders, they’re a way to handle doing tasks off the UI thread. The benefits include having a listener that will inform your fragment/activity when the data your monitoring is changed. They will also account for orientation changes when loading.

asynctaskloader using support library example

A brief introduction is written here and it got me up to speed: An Intro to Loaders

This tutorial has extensive code but is pretty simple in concept. It follows on from the tutorial already written at http://developer.android.com/reference/android/content/AsyncTaskLoader.html. The Google tutorial shows you how to use an ASyncTaskLoader but with the post HoneyComb API. So I’ve taken this code and used it but coded against the support library.

There are two changes and one bug fix that need to be implemented to get this to work! I’ve also increased the OO concepts using correct packaging and class files.

  • Changed out the HC ArrayAdapter and used BaseAdapter
  • Removed the use of the ActionBar search function
  • ASyncTask wont start unless you explicity call forceLoad()!

First off we can’t use the helper methods in extending HC ArrayAdapter, we extend BaseAdapter and do the data management ourselves. In the long run this is probably a better approach for control and customisation.

AppListAdapter.java

package com.blundell.asynctaskloader.ui.adapter;

import java.util.List;

import com.blundell.asynctaskloader.R;
import com.blundell.asynctaskloader.domain.AppEntry;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class AppListAdapter extends BaseAdapter {

	private final LayoutInflater inflater;
	private List<AppEntry> data;

	public AppListAdapter(Context context) {
		inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	}

	public void setData(List<AppEntry> data){
		this.data = data;
		notifyDataSetChanged();
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		View view;

		if(convertView == null){
			view = inflater.inflate(R.layout.list_item_icon_text, parent, false);
		} else {
			view = convertView;
		}

		AppEntry item = data.get(position);
		((ImageView) view.findViewById(R.id.icon)).setImageDrawable(item.getIcon());
		((TextView) view.findViewById(R.id.text)).setText(item.getLabel());

		return view;
	}

	@Override
	public int getCount() {
		return data == null ? 0 : data.size();
	}

	@Override
	public Object getItem(int position) {
		return data == null ? null : data.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

}

I removed the ActionBar search functionality, I don’t think it should be part of this tutorial and just stops you seeing the wood for the trees as they say. So here’s the new fragment.

AppListFragment.java

package com.blundell.asynctaskloader.ui.fragment;

import java.util.List;

import com.blundell.asynctaskloader.domain.AppEntry;
import com.blundell.asynctaskloader.ui.adapter.AppListAdapter;
import com.blundell.asynctaskloader.ui.loader.AppListLoader;

import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;

public class AppListFragment extends ListFragment implements LoaderCallbacks<List<AppEntry>> {

	private AppListAdapter adapter;

	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);

		setEmptyText("No applications");

		adapter = new AppListAdapter(getActivity());
		setListAdapter(adapter);

		setListShown(false);

		// Prepare the loader
		// either reconnect with an existing one or start a new one
		getLoaderManager().initLoader(0, null, this);
	}

	@Override
	public void onListItemClick(ListView l, View v, int position, long id) {
		Toast.makeText(getActivity(), "Clicked: "+ id, Toast.LENGTH_SHORT).show();
	}

	@Override
	public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) {
		// This is called when a new loader needs to be created.
		// This sample only has one Loader with no arguments, so it is simple.
		return new AppListLoader(getActivity());
	}

	@Override
	public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) {
		adapter.setData(data);

		// The list should now be shown
		if(isResumed()){
			setListShown(true);
		} else {
			setListShownNoAnimation(true);
		}
	}

	@Override
	public void onLoaderReset(Loader<List<AppEntry>> loader) {
		// Clear the data in the adapter
		adapter.setData(null);
	}
}

Finally there is a bug when extending ASyncTaskLoader from the support library, the loader doesn’t actually start. It’s written up as an issue here. What you need to do is catch when the loader wants to start loading using onStartLoading(), then query if you have your data yet (because the loader could be restarted) if you have then deliver your data, else force it to start.

package com.blundell.asynctaskloader.ui.loader;

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

import com.blundell.asynctaskloader.domain.AppEntry;
import com.blundell.asynctaskloader.receiver.PackageIntentReceiver;
import com.blundell.asynctaskloader.util.Comparator;

import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.support.v4.content.AsyncTaskLoader;

public class AppListLoader extends AsyncTaskLoader<List<AppEntry>> {

	InterestingConfigChanges lastConfig = new InterestingConfigChanges();
	public final PackageManager pm;

	List<AppEntry> apps;
	PackageIntentReceiver packageObserver;

	public AppListLoader(Context context) {
		super(context);

		// Retrieve the package manager for later use; note we don't
        // use 'context' directly but instead the safe global application
        // context returned by getContext().
		pm = getContext().getPackageManager();
	}


	@Override
	protected void onStartLoading() {
		super.onStartLoading();
		// AsyncTaskLoader doesn't start unless you forceLoad http://code.google.com/p/android/issues/detail?id=14944
		if(apps != null){
			deliverResult(apps);
		}
		if(takeContentChanged() || apps == null){
			forceLoad();
		}
	}


	/**
	 * This is where the bulk of the work. This function is called in a background thread
	 * and should generate a new set of data to be published by the loader.
	 */
	@Override
	public List<AppEntry> loadInBackground() {
		// Retrieve all known applications
		List<ApplicationInfo> apps = pm.getInstalledApplications(
											PackageManager.GET_UNINSTALLED_PACKAGES |
											PackageManager.GET_DISABLED_COMPONENTS);

		if(apps == null){
			apps = new ArrayList<ApplicationInfo>();
		}

		final Context context = getContext();

		// Create corresponding array of entries and load their labels
		List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
		for (ApplicationInfo applicationInfo : apps) {
			AppEntry entry = new AppEntry(this, applicationInfo);
			entry.loadLabel(context);
			entries.add(entry);
		}

		Collections.sort(entries, Comparator.ALPHA_COMPARATOR);

		return entries;
	}

	/**
	 * Called when there is new data to deliver to the client. The super class
	 * will take care of delivering it; the implementation just adds a little more logic
	 */
	@Override
	public void deliverResult(List<AppEntry> apps) {
		if(isReset()){
			// An async query came in while the loader is stopped. We don't need the result
			if(apps != null){
				onReleaseResources(apps);
			}
		}
		List<AppEntry> oldApps = this.apps;
		this.apps = apps;

		if(isStarted()){
			// If the loader is currently started, we can immediately deliver a result
			super.deliverResult(apps);
		}

		// At this point we can release the resources associated with 'oldApps' if needed;
		// now that the new result is delivered we know that it is no longer in use
		if(oldApps != null){
			onReleaseResources(oldApps);
		}
	}

	@Override
	protected void onStopLoading() {
		// Attempts to cancel the current load task if possible
		cancelLoad();
	}

	@Override
	public void onCanceled(List<AppEntry> apps) {
		super.onCanceled(apps);

		// At this point we can release the resources associated with 'apps' if needed
		onReleaseResources(apps);
	}

	/**
	 * Handles request to completely reset the loader
	 */
	@Override
	protected void onReset() {
		super.onReset();

		// ensure the loader is stopped
		onStopLoading();

		// At this point we can release the resources associated with 'apps' if needed
		if(apps != null){
			onReleaseResources(apps);
			apps = null;
		}

		// Stop monitoring for changes
		if(packageObserver != null){
			getContext().unregisterReceiver(packageObserver);
			packageObserver =  null;
		}
	}

	/**
	 * Helper function to take care of releasing resources associated with an actively loaded data set
	 */
	private void onReleaseResources(List<AppEntry> apps){
		// For a simple list there is nothing to do
		// but for a Cursor we would close it here
	}

	public static class InterestingConfigChanges {
		final Configuration lastConfiguration = new Configuration();
		int lastDensity;

		protected boolean applyNewConfig(Resources res){
			int configChanges = lastConfiguration.updateFrom(res.getConfiguration());
			boolean densityChanged = lastDensity != res.getDisplayMetrics().densityDpi;
			if(densityChanged || (configChanges & (ActivityInfo.CONFIG_LOCALE|ActivityInfo.CONFIG_UI_MODE|ActivityInfo.CONFIG_SCREEN_LAYOUT)) != 0){
				lastDensity = res.getDisplayMetrics().densityDpi;
				return true;
			}
			return false;
		}
	}
}

That should do it, if you run up your app you’ll see a list of all the applications running on your device. I also changed the fragment to be instantiated in XML instead of with the fragment manager. This was only done in the Google example because it’s part of a bigger package of examples.

activity_main.xml

<RelativeLayout 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" >

    <fragment
        android:id="@+id/fragment_app_list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        class="com.blundell.asynctaskloader.ui.fragment.AppListFragment" />

</RelativeLayout>

For the full source code see below:

Download link for the eclipse project source

GitHub Repo Mirror