Android: Activity Lifecycle and Threads

Android is allowed to destroy your activity at anytime. Also a configuration change will restart your activity to load the right resources for this configuration.

If you use Threads in your Android program you should save them. Nothing is more annoying then having a running download, flipping the phone and finding that the download was canceled.

Based on my Creating Bitmaps from the internet via Apache HttpClient example here is an Activity which saves the Thread which downloads the image and also makes sure the dialog is correctly re-created after restart.

package de.vogella.android.threadslifecycle;

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

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

public class ThreadsLifecycleActivity extends Activity {
	// Static so that the thread access the latest attribute
	private static ProgressDialog dialog;
	private static ImageView imageView;
	private static Bitmap downloadBitmap;
	private static Handler handler;
	private Thread downloadThread;

	/** Called when the activity is first created. */

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// Create a handler to update the UI
		handler = new Handler();
		// get the latest imageView after restart of the application
		imageView = (ImageView) findViewById(R.id.imageView1);
		// Did we already download the image?
		if (downloadBitmap != null) {
			imageView.setImageBitmap(downloadBitmap);
		}
		// Check if the thread is already running
		downloadThread = (Thread) getLastNonConfigurationInstance();
		if (downloadThread != null && downloadThread.isAlive()) {
			dialog = ProgressDialog.show(this, "Download", "downloading");
		}
	}

	public void resetPicture(View view) {
		if (downloadBitmap != null) {
			downloadBitmap = null;
		}
		imageView.setImageResource(R.drawable.icon);
	}

	public void downloadPicture(View view) {
		dialog = ProgressDialog.show(this, "Download", "downloading");
		downloadThread = new MyThread();
		downloadThread.start();
	}

	// Save the thread
	@Override
	public Object onRetainNonConfigurationInstance() {
		return downloadThread;
	}

	// dismiss dialog if activity is destroyed
	@Override
	protected void onDestroy() {
		if (dialog != null && dialog.isShowing()) {
			dialog.dismiss();
			dialog = null;
		}
		super.onDestroy();
	}

	// Utiliy method to download image from the internet
	static private Bitmap downloadBitmap(String url) throws IOException {
		HttpUriRequest request = new HttpGet(url.toString());
		HttpClient httpClient = new DefaultHttpClient();
		HttpResponse response = httpClient.execute(request);

		StatusLine statusLine = response.getStatusLine();
		int statusCode = statusLine.getStatusCode();
		if (statusCode == 200) {
			HttpEntity entity = response.getEntity();
			byte[] bytes = EntityUtils.toByteArray(entity);

			Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0,
					bytes.length);
			return bitmap;
		} else {
			throw new IOException("Download failed, HTTP response code "
					+ statusCode + " - " + statusLine.getReasonPhrase());
		}
	}

	static public class MyThread extends Thread {
		@Override
		public void run() {
			try {
				// Simulate a slow network
				try {
					new Thread().sleep(5000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				downloadBitmap = downloadBitmap("http://www.vogella.de/img/lars/LarsVogelArticle7.png");
				handler.post(new MyRunnable());
			} catch (IOException e) {
				e.printStackTrace();
			} finally {

			}
		}
	}

	static public class MyRunnable implements Runnable {
		public void run() {
			imageView.setImageBitmap(downloadBitmap);
			dialog.dismiss();
		}
	}
}

If you run this you can test via CNTR+F11 to trigger a configuration change. You find more details in my Android Threads, Handlers and AsyncTask – Tutorial .

I big thanks to Mark Murphy who spotted a memory leak in the first version of the example.

I hope this helps. You find me also on Twitter. My profile can be found .

About Lars Vogel

Lars Vogel is the founder and CEO of the vogella GmbH and works as Eclipse and Android consultant, trainer and book author. He is a regular speaker at international conferences, He is the primary author of vogella.com. With more than one million visitors per month this website is one of the central sources for Java, Eclipse and Android programming information.
This entry was posted in Android and tagged . Bookmark the permalink.