Introduction

After SDK 1.5, an API was made available for executing asynchronous tasks in Android Activities, without worrying with threads and manners to access the UI. For that, one must use AsyncTasks. Here a simple example is provided of how to properly use this API. Later, it will be discussed when to use (and when not to use) this API.

Background

In order to understand the importance of this API, one has to realize that when executing long tasks in Android Activities, the UI cannot be locked while the task is being performed. The UI has to be accessible so the user does not have the impression that the program being executed is frozen. Furthermore, when executing time-consuming tasks, the user may have to be informed of the progress of this very task. Thus, to accomplish all these goals, one has to utilize the AsyncTasks API, which allows a long job to be executed while its progress is being displayed and the interface is still accessible for the user to interact.

Using the Code

In order to use the AsyncTask API, one has to follow the steps described below:

Implement the method doInBackground(Parameters... parameters). This method must execute the job which is supposed to be quite demanding.

Optionally, one can implement methods for:

cancelling the task - onCancelled(...)

executing tasks before the demanding task - onPreExecute(...)

reporting progress - onProgressUpdate(...)

executing activities after the demanding task is finished -onPostExecute(...).

Having that in mind, now a sample will be given which the code shows what was just enumerated above.

Displayed below is the XML Layout of the Activity which holds the long-task to-be-executed. This activity has a Start/Restart and Cancel buttons, which obviously starts and cancels the task respectively. It also holds a label, which states the task status and a progress bar which actually shows the progress in terms or complete percentage.

Given the Activity´s XML Layout, the Activity implementation will be depicted below. Note that the code is widely commented so one can easily understand it. The full application is also available for download in this article. Now, further explanations will be given regarding the most important parts of the code.

package com.asynctask;
import java.text.MessageFormat;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
publicclass MainActivity extends Activity {
private Button btnRestart;
private Button btnCancel = null;
private TextView txtMessage = null;
private ProgressBar mProgressBar = null;
private HugeWork task = null;
privatestaticfinalint MAX_PROGRESS = 10;
@Overridepublicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnRestart = (Button) findViewById(R.id.btnRestart);
btnCancel = (Button) findViewById(R.id.btnCancel);
txtMessage = (TextView) findViewById(R.id.txtMessage);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
// set an arbitrary max value for the progress bar
mProgressBar.setMax(MAX_PROGRESS);
// start the async task
start();
}
// Cancel the async task and handle buttons enablement. Note that
// the Cancel button is disabled because the task is finished and the
// restart button is enabled so one can execute the process again.
//// this is the listener for the Cancel Button.
publicvoid cancelOnclick(View v) {
task.cancel(true);
btnCancel.setEnabled(false);
btnRestart.setEnabled(true);
}
// Restart the process execution. This is the listener to the Restart button.
publicvoid restartOnclick(View v) {
start();
}
// Here we start the big task. For that, we reset the progress bar, set the
// cancel button to be enable so one can stop the operation at any time and
// finally we disable the restart button because the task is on-going.
privatevoid start() {
// instantiate a new async task
task = new HugeWork();
// start async task setting the progress to zero
task.execute(0);
// reset progress
mProgressBar.setProgress(0);
// handle buttons
btnCancel.setEnabled(true);
btnRestart.setEnabled(false);
}
// execute the hard will which will take a lot of time. For our example,
// 1 second.
privatevoid executeHardWork() {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
// This class implements the methods for an async task to be executed
// The only required method is the doInBackground(Params... params). This
// method execute the big job in background. The other methods are not
// required but they are implemented here so you can better understand how
// tye work.
//// Note that this class has three generic types assigned to Integer. These
// types represents the arguments of the implemented methods.
//// The first one, is passed when the async task is executed. It is an array
// of necessary elements to be passed for the async task to be executed, in
// case there is a need to do so. This parameter is used in the method doInBackground(...).
//// The second parameter is eh type used for progress.
// Thus, when onProgressUpdate(...) is called,
// the parameters for this methods are of the type of this second parameter.
//// The third parameter is used for when the task is complete.
// Note that this parameter is the
// return type of the method doInBackground(...) and
// the parameter of the methods onPostExecute(...)
// and onCancelled(...).
class HugeWork extends AsyncTask<Integer, Integer, Integer> {
// Method executed before the async task start. All things needed to be
// setup before the async task must be done here. In this example we
// simply display a message.
@Overrideprotectedvoid onPreExecute() {
txtMessage.setText("Executing async task...");
super.onPreExecute();
}
// Here is where all the hard work is done. We simulate it by executing
// a sleep for 1 second, 10 times. Each time the sleep is performed, we update
// our progress in the method publishProgress(...). This method executes the
// overridden method onProgressUpdate(...) which updates the progress.
@Overrideprotected Integer doInBackground(Integer... params) {
// get the initial parameters. For us, this is the initial bar progress = 0
int progress = ((Integer[])params)[0];
do {
// only keep going in case the task was not cancelled
if (!this.isCancelled()) {
// execute hard work - sleep
executeHardWork();
}
else {
// in case the task was cancelled, break the loop
// and finish this task
break;
}
// upgrade progress
progress++;
publishProgress(progress);
} while (progress <= MAX_PROGRESS);
return progress;
}
// Every time the progress is informed, we update the progress bar
@Overrideprotectedvoid onProgressUpdate(Integer... values) {
int progress = ((Integer[])values)[0];
mProgressBar.setProgress(progress);
super.onProgressUpdate(values);
}
// If the cancellation occurs, set the message informing so
@Overrideprotectedvoid onCancelled(Integer result) {
txtMessage.setText(MessageFormat.format
("Async task has been cancelled at {0} seconds.", result - 1));
super.onCancelled(result);
}
// Method executed after the task is finished. If the task is cancelled this method is not
// called. Here we display a finishing message and arrange the buttons.
@Overrideprotectedvoid onPostExecute(Integer result) {
txtMessage.setText(MessageFormat.format
("Async task execution finished in {0} seconds.", result - 1));
btnCancel.setEnabled(false);
btnRestart.setEnabled(true);
super.onPostExecute(result);
}
}
}

The method shown below represents the core of the asynchronous functionality. This method executes the long-time task (calling the time-consuming-method executeHardWork()) and report its progress using the method publishProgress(progress) - which makes the event onProgressUpdate(...) to be executed.. Note also that here it is verified whether the task was cancelled, before it proceeds.

Thus, here all which is desired for a long-period-asynchronous task execution is accomplished. The job does not freeze the UI, it can be cancelled and its progress is reported.

// Here is where all the hard work is done. We simulate it by executing
// a sleep for 1 second, 10 times. Each time the sleep is performed, we update
// our progress in the method publishProgress(...). This method executes the
// overridden method onProgressUpdate(...) which updates the progress.
@Overrideprotected Integer doInBackground(Integer... params) {
// get the initial parameters. For us, this is the initial bar progress = 0
int progress = ((Integer[])params)[0];
do {
// only keep going in case the task was not cancelled
if (!this.isCancelled()) {
// execute hard work - sleep
executeHardWork();
}
else {
// in case the task was cancelled, break the loop
// and finish this task
break;
}
// upgrade progress
progress++;
publishProgress(progress);
} while (progress <= MAX_PROGRESS);
return progress;
}

Continuing, have a look at the method displayed below. It interacts with the UI displaying the progress made. Note that the percentage of the progress is indeed informed by the variable progress. Moreover, observe that this method, the onProgressUpdate(...) was called by the publishProgress(progress), as mentioned earlier.

Another method worth mentioning is the onCancelled(Integer). This event is called and the cancellation is performed. This method simply displays a message stating the new status (cancellation). Notice also, that when the cancellation is dispatched, the method doInBackground(...) stops executing when the isCancelled() verification returns true. It is important to emphasize that, when the call off occurs, the method onPostExecute(...) is not called. In the code, the cancellation was triggered by the Cancel Button event cancelOnclick(View). The method called in the AsyncTask object was AsyncTask.cancel(boolean).

// If the cancellation occurs, set the message informing so
@Overrideprotectedvoid onCancelled(Integer result) {
txtMessage.setText(MessageFormat.format
("Async task has been cancelled at {0} seconds.", result - 1));
super.onCancelled(result);
}

The last method discussed is the onPostExecute(Integer). It executes the necessary work after the task is finished. Here it displays a finish message and resets the form so the job can be started over. Again, this method is not called when the task is cancelled, as stated before.

// Method executed after the task is finished. If the task is cancelled this method is not
// called. Here we display a finishing message and arrange the buttons.
@Overrideprotectedvoid onPostExecute(Integer result) {
txtMessage.setText(MessageFormat.format
("Async task execution finished in {0} seconds.", result - 1));
btnCancel.setEnabled(false);
btnRestart.setEnabled(true);
super.onPostExecute(result);
}

When to Use this API

After reading this article, one may be wondering whether this manner of interacting with the UI should be used with every action taken by the user. For instance, suppose your UI displays a list of items and you can edit, delete, update or make changes in a set of them. For each of these actions, one may be inclined to use this API, believing that by doing so, there will be no risk of the user interacting with a frozen form. The answer to this issue is: no, one does not have to use this API for each user action, and here are the reasons:

Most of the user actions will have a quick response, thus making it much simpler to just implement the event without worrying about frozen windows.

The code is much harder to maintain. Think about the situation just described where one has a list in which many actions can be taken. The code would be tediously repetitive, prompted to many bugs and most of the code written would be rendered useless because there is no need for fancy asynchronous interactions.

However, one should be aware of the scenarios where this API is mandatory for a good user experience. These cases fall into a simple rule, which is obvious but deserves to be discussed: whenever an action will take too long - more than one or two seconds - the user must be warned that a job is still being executed. Here are the cases where these situations are most likely to happen:

The activity involves network communication. Whenever the application has to interact with the internet or local network, there most likely will be a delay. Thus one should use the AsyncTaskAPI.

The application is starting and heavy processing is being made. This occurs mainly in games. Note that when most of the fanciest games open, they take a while to start. This is the ideal scenario to show a progress bar, a label stating what is happening, and a waiting message.

Conclusions

This article explained in details how the Android API for asynchronous tasks works. This API is very interesting because the resulting code is quite organized and something which could be quite hard to implement using threads is straightforward and easy to maintain. Finally, here we also discussed when it is appropriate to use this API and when it is not.