Tag Archives: doInBackground

Android AsyncTask

When an Android app starts, everything is running within a single thread called the main thread or UI thread. The name ‘UI thread’ comes from the fact that it is this thread which handles all interaction with the user interface. UI controls are not, in general, thread-safe, meaning that an attempt to modify them from a non-UI thread can cause things to break. As a result, you should always ensure that any use of UI components is run on the UI thread.

If our app contains code that can take a long time to run (such as downloading something off the internet), putting this code in the UI thread will block that thread until the task completes. A blocked UI thread means that none of the controls will respond to user input, making it appear that the app has hung.

Android provides the AsyncTask generic class to help with this problem. AsyncTask allows a lengthy task to be run in a background thread, but provides methods in which progress reports and a final result can be posted to the UI thread. The AsyncTask generic class has the form AsyncTask<Params, Progress, Result>, where:

  • Params is the data type of objects sent to the AsyncTask as input data
  • Progress is the data type of objects sent as progress reports while the task is running
  • Result is the data type of the final object, delivered after the task finishes.

To use AsyncTask, you must define your own class that inherits it and overrides the doInBackground() method (and possibly a few other methods).

The easiest way to see how AsyncTask works is to look at an example. We’ve created an app which lets the user enter a positive integer, and the app will then calculate all the prime numbers up to that integer. The calculation is done in an AsyncTask-derived class called CalcPrimesTask. Params (the input) consists of a single integer. We’ll deliver a progress report, in the form of a String, after each prime is found. Finally, we’ll return a List containing all the primes as the Result at the end.

We’ve made the calculation a long job by putting a 1 second delay after each prime found.

The UI provides an EditText for entry of the maximum number, a Start button to a new calculation (pressing the Start button adds a new job to the queue on each press), a Stop button to stop all tasks, and a TextView for displaying messages. Here’s the MainActivity that starts the app:

package growe.ex07asynctask;

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

import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import android.app.Activity;

public class MainActivity extends Activity {
	List<CalcPrimesTask> taskList;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		taskList = new ArrayList<CalcPrimesTask>();
	}

	public void onClickStart(View view) {
		EditText maximumEditText = (EditText) findViewById(R.id.maximumEditText);
		int maxNum = Integer.parseInt(maximumEditText.getText().toString());
		CalcPrimesTask task = new CalcPrimesTask(this);
		taskList.add(task);
		task.execute(maxNum);
		Toast.makeText(getApplicationContext(), "New run queued.", Toast.LENGTH_SHORT).show();
	}

	public void onStopClick(View view) {
		for (CalcPrimesTask task : taskList) {
			task.cancel(true);
		}
		Toast.makeText(getApplicationContext(), "All runs cancelled.", Toast.LENGTH_SHORT).show();
	}
}

In onCreate() on line 19 we create the list of tasks. The event handler for the Start button is on line 22, where we retrieve the number entered and then start up a new CalcPrimesTask to do the calculation. This task is added to the ArrayList and then the execute() method is called, which starts the task running. We also display a Toast message when the job starts. (If we hadn’t used a separate thread for the calculation, this Toast message wouldn’t appear until after the job finished because the UI thread would be blocked.)

The onStopClick() method on line 31 just calls cancel() on all tasks and displays another Toast message. The ‘true’ in the call to cancel() indicates that even if the task is currently running, it should be interrupted, if possible.

Now let’s look at CalcPrimesTask:

package growe.ex07asynctask;

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

import android.app.Activity;
import android.os.AsyncTask;
import android.widget.TextView;

public class CalcPrimesTask extends AsyncTask<Integer, String, List<Integer>> {

	Activity activity;

	public CalcPrimesTask(Activity mainActivity) {
		activity = mainActivity;
	}

	@Override
	protected List<Integer> doInBackground(Integer... params) {
		int maxNum = params[0];
		List<Integer> primeList = new ArrayList<Integer>();
		for (int i = 2; i <= maxNum ; i++) {
			int maxCalc = (int)Math.sqrt(i);
			boolean isPrime = true;
			for (int j = 2; j <= maxCalc ; j++) {
				if (i % j == 0) {
					isPrime = false;
					break;
				}
			}
			if (isPrime) {
				primeList.add(i);
				publishProgress("Prime " + i + " found.");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
				}
			}
		}
		return primeList;
	}

	@Override
	protected void onProgressUpdate(String... values) {
		TextView messageView = (TextView) activity.findViewById(R.id.messageText);
		messageView.setText(values[0]);
		super.onProgressUpdate(values);
	}

	@Override
	protected void onPostExecute(List<Integer> result) {
		TextView messageView = (TextView) activity.findViewById(R.id.messageText);
		messageView.setText("Total of " + result.size() + " primes found.");
		super.onPostExecute(result);
	}
}

On line 10, we define CalcPrimesTask by providing the generic types for AsyncTask as described above.

We need to pass in the parent Activity so we can access the UI views, so we store a reference to the Activity in the constructor on line 15.

The one method we must override is doInBackground() (line 19). It takes a sequence of Integers (the parameter type Integer… means an arbitrarily large list of Integers) which is the Params type declared on line 10, and returns a List<Integer>, which is the Result type. Code within doInBackground() is run in the background or worker thread, not the UI thread, so this is where the lengthy calculation goes. The code here creates a List<Integer> in which to store the primes and uses a simple test to find all the primes up to the value entered by the user. This value was the maxNum argument to the execute() method in MainActivity (line 27), and appears as the first element in the params array, so we access it on line 20. To test if a number i is prime, we find its square root and then test to see if all numbers from 2 up to the square root divide the number. If not, the number is prime, so we add it to the list (line 32). At this point, we want to publish a progress report back to the UI to let the user know a prime was found. We can do this with the publishProgress() method (inherited from AsyncTask), whose argument must of the type Progress (a String here). publishProgress() calls onProgressUpdate() which is run on the UI thread, so it’s safe to access UI views here. We update the TextView with the message that a prime has been found.

When the background task is finished, doInBackground() must return an object of type Result (which is a List<Integer> here). This Result object is sent to onPostExecute(), which also runs in the UI thread. Here (on line 53) we just print out how many primes were found, although obviously we could have used the list to display the primes or do something else with them.

There is another method called onPreExecute() which can be overridden. It too runs on the UI thread and is called before doInBackground() starts up the background thread. It doesn’t take any arguments, however, so you can’t pass any data to it.

Advertisements