Android's indeterminate ProgressDialog tutorial

Delve into how to use the Android ProgressDialog class to create a better user experience.

In my dream world, a user would never have to wait on an application to fetch data from a server. Sadly, while the digital pipelines that feed the latest and greatest mobile apps continue to widen, we haven't quite reached the point of instantaneous data.

When it comes to writing mobile applications, there are a number of tricks-of-the-trade to shield the user from much of the tedious crunching that goes on in the background. There are still times when the better user experience (UX) is to display a "please wait" dialog, rather than leave the user scratching his head and wondering what is going on.

Fortunately, the Android framework has a number of built-in classes to handle these situations. One such class is the ProgressDialog. The example that follows displays a dialog and spinner while some background work goes on. As is standard practice on the Android platform, any background work needs to be moved off of the user interface (UI) thread. This is generally accomplished with the AsyncTask class.

Feel free to follow along with the step-by-step tutorial below, or, download and import the entire project into Eclipse.

1. Create a new Android project in Eclipse. Target Android 1.6 or greater. Be sure to rename the startup activity to and the associated layout to main.xml.

2. In the /res folder, create a linear layout that holds a single text field and button.

<LinearLayout xmlns:android=<em>""</em>
<em>        </em><span style="text-decoration: underline;">android:text=<em>"Progress Dialog Demo"</em></span> />
        <span style="text-decoration: underline;">android:text=<em>"Do Background Processing"</em></span>
        android:id=<em>"@+id/the_button"</em> />

3. In the /src folder, modify In the on create override, assign the activity context and wire up our button listener.

package com.authorwjf.progressdialog;

import com.wjf.progressdialog.R;

import android.os.AsyncTask;
import android.os.Bundle;
import android.content.Context;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class Main extends Activity implements OnClickListener{
	private Context context;
	private ProgressDialog pd;
	private Button b;

    public void onCreate(Bundle savedInstanceState) {
        context = this;
        b = (Button) findViewById(;
    protected void onDestroy() {
    	if (pd!=null) {

4. The on click view handler is responsible for disabling the button and then spawning a new AsyncTask that will handle the background work. In the pre and post execute methods, we worry about the ProgressDialog and re-enabling the button.

public void onClick(View v) {
	AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
		protected void onPreExecute() {
			pd = new ProgressDialog(context);
			pd.setMessage("Please wait.");
		protected Void doInBackground(Void... arg0) {
			try {
				//Do something...
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
			return null;
		protected void onPostExecute(Void result) {
			if (pd!=null) {

Now our demo is ready to run. Before loading it to a device, take a closer look at the onPreExecute method of the AsyncTask. Notice how we set up a number of properties on the ProgressDialog? The title and message properties are fairly self-explanatory, but you may be curious about the cancelable and indeterminate properties. The first property specifies whether the user can cancel the dialog by tapping outside of it. The second property tells the dialog whether to display a spinner. If false, the ProgressDialog will contain a real progress bar, and you will need to periodically update the UI with the current completion percent of your background task.

Indeterminate progress dialogs should never be your first UX choice. But don't be afraid to use them when the alternative is to leave the user wondering if your application has suffered cardiac arrest.

Note: This tutorial is focused on the progress dialog and as such ignores what happens to the long-running I/O itself when an orientation takes place. I’ve written another tutorial on long-running I/O that delves into passing asynchronous tasks between life cycle events