Android Threading: Part 1

The UI layer of all Android applications runs on a single thread called the UI thread.

Performing long-running operations such as network access or database queries on this thread will block the user interface, and eventually crash your application with an Application Not Responding (ANR) error.

In this tutorial I’ll show you a simple way to perform a long-running task in the background using the AsyncTask class to download an image.

To start, create a new application and update the main.xml layout file with this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/btnDownloadImages"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/helloText"
        android:onClick="downloadImage"
        android:text="Tap to Download Image" />

    <ImageView
        android:id="@+id/imgDownload"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@+id/helloText" />

</LinearLayout>

When a user taps the button, we’ll:

  1. Display a busy dialog
  2. Invoke an AsyncTask to download an image from the internet
  3. Display the downloaded image in the ImageView control
  4. Hide the busy dialog

Here’s the Activity class to achieve this (note the comments):

package com.justinschultz.asynctaskexample;

import java.io.InputStream;
import java.net.URL;

import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

public class AsyncTaskExampleActivity extends Activity {
	ImageView _imgView = null;
	ProgressDialog _busyDialog = null;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// Need an Activity-level reference to the ImageView
		_imgView = (ImageView) findViewById(R.id.imgDownload);
	}

	public void downloadImage(View v) {
		// Need to show the busy dialog here, since we can't do it in
		// DownloadImageTask's doInBackground() method
		showBusyDialog();
		new DownloadImageTask()
				.execute("http://code.google.com/android/goodies/wallpaper/android-wallpaper5_1024x768.jpg");
	}

	private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
		@Override
		protected Bitmap doInBackground(String... url) {
			// You might be tempted to display the busy dialog here, but you
			// CAN'T update the UI from this method!
			return loadImageFromNetwork(url[0]);
		}

		@Override
		protected void onPostExecute(Bitmap result) {
			// Our long-running process is done, and we can safely access the UI thread
			dismissBusyDialog();
			_imgView.setImageBitmap(result);
		}
	}

	private Bitmap loadImageFromNetwork(String url) {
		Bitmap bitmap = null;

		try {
			bitmap = BitmapFactory.decodeStream((InputStream) new URL(url).getContent());
		} catch (Exception e) {
			e.printStackTrace();
		}

		return bitmap;
	}

	public void showBusyDialog() {
		_busyDialog = ProgressDialog.show(this, "", "Downloading Image...", true);
	}

	public void dismissBusyDialog() {
		if (_busyDialog != null) {
			_busyDialog.dismiss();
		}
	}
}

The AsyncTask class must be subclassed, and it’s 3 generic types are:

  1. Params: the type of the parameters sent to the task upon execution.
  2. Progress: the type of the progress units published during the background computation.
  3. Result: the type of the result of the background computation.

Not all types are always used by an asynchronous task. To mark a type as unused, simply use the type Void:

private class MyTask extends AsyncTask<Void, Void, Void> { ... }

I don’t recommend using AsyncTask for things like long-running API calls, but it’s fine for short tasks within the activity life (between onResume and onPause). I’ll cover approaches to long-running API calls in a future post.

Download the complete code for this example here.


About this entry