The mechanims to execute this API is the following:
- Create a class which extends AsyncTask.
- Fill in the generic types available as generics in the class for:
- the task execution array parameters
- progress array parameters
- result array parameters
- Implement the method doInBackground(Parameters... parameters). This method must execute the job which is supposed to be quite demanding.
- Optionally, on can implement methods for:
- cancelling the task - onCancelled(...)
- executing tasks before the demanding task - onPreExecute(...)
- reporting progress - onProgressUpdate(...)
- execute activities after the demanding task is finished - onPostExecute(...).
Android Layout XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/txtMessage"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="304dp"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/btnRestart"
android:onClick="restartOnclick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Restart" />
<Button
android:id="@+id/btnCancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="cancelOnclick"
android:text="Cancel" />
</LinearLayout>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/txtMessage"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="304dp"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/btnRestart"
android:onClick="restartOnclick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Restart" />
<Button
android:id="@+id/btnCancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="cancelOnclick"
android:text="Cancel" />
</LinearLayout>
</LinearLayout>
Activity Implementation
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;
public class MainActivity extends Activity {
private Button btnRestart;
private Button btnCancel = null;
private TextView txtMessage = null;
private ProgressBar mProgressBar = null;
private HugeWork task = null;
private static final int MAX_PROGRESS = 10;
@Override
public void 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.
public void cancelOnclick(View v) {
task.cancel(true);
btnCancel.setEnabled(false);
btnRestart.setEnabled(true);
}
// Restart the process execution. This is the listener to the Restart button.
public void 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.
private void 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.
private void 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.
@Override
protected void 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.
@Override
protected 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
@Override
protected void onProgressUpdate(Integer... values) {
int progress = ((Integer[])values)[0];
mProgressBar.setProgress(progress);
super.onProgressUpdate(values);
}
// If the cancellation occurs, set the message informing so
@Override
protected void 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.
@Override
protected void 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);
}
}
}
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;
public class MainActivity extends Activity {
private Button btnRestart;
private Button btnCancel = null;
private TextView txtMessage = null;
private ProgressBar mProgressBar = null;
private HugeWork task = null;
private static final int MAX_PROGRESS = 10;
@Override
public void 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.
public void cancelOnclick(View v) {
task.cancel(true);
btnCancel.setEnabled(false);
btnRestart.setEnabled(true);
}
// Restart the process execution. This is the listener to the Restart button.
public void 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.
private void 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.
private void 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.
@Override
protected void 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.
@Override
protected 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
@Override
protected void onProgressUpdate(Integer... values) {
int progress = ((Integer[])values)[0];
mProgressBar.setProgress(progress);
super.onProgressUpdate(values);
}
// If the cancellation occurs, set the message informing so
@Override
protected void 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.
@Override
protected void 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);
}
}
}
One interesting discussion is the reason for AsyncTasks. Imagine you have a task which takes a long time, for instance 3 seconds. If you simply execute this task without any UI treatment, the mobile window will freeze and the user will probably think the application has crashed. To avoid so, the UI should display a message, alternatively with a progress bar, stating the application is running but it is waiting for a task to finish. This increases the User Experience and makes your application much more trustworthy.
The code above does that. It executes a simulated long task (10 seconds) informing the user of the progress. Note that the AsyncTasks provides a nice way of treating the problem because it emcapsulates the the asynchronous task execution. Morover, using this API one does not have to worry where is the correct place to treat UI events, because one is made available - onProgressUpdate(...).
Now, the code will be explained in more details. See the code below:
// 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.
@Override
protected 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;
}
Here is whee the "magic" happens. This method executes the long-time task (calling the 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 poceeds.
Now, have a look at the method below:
// Every time the progress is informed, we update the progress bar
@Override
protected void onProgressUpdate(Integer... values) {
int progress = ((Integer[])values)[0];
mProgressBar.setProgress(progress);
super.onProgressUpdate(values);
}
This event is called, as mentioned before, if the method publishProgress(progress) is executed. The method onProgressUpdate(...) simply updates the progress into the progress bar.
Another important event is displayed below:
// If the cancellation occurs, set the message informing so
@Override
protected void onCancelled(Integer result) {
txtMessage.setText(MessageFormat.format("Async task has been cancelled at {0} seconds.", result - 1));
super.onCancelled(result);
}
This event is called and the cancellation is perfomed. 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 v). The method called in the AsyncTask object was AsyncTask.cancel(boolean).
It is also important to discuss the method is displayed below:
// 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.
@Override
protected void 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);
}
Here tasks are executed after the long-time task is executed. For this case, the buttons are redefined and a message is displayed.
If you think about the code, the methods explained above are the more important ones. Note that the code provided is complete and well commented. Therefore, I strongly advise you to create an android project and insert the code into it. You will be able to observe how it works properly and see it in action.
Great post with nice example Fernandes... Thanku so much.. Even thishttp://androidtechstuffs.blogspot.in/2013/01/why-and-how-to-use-asynctask.html is also helpful. have a look..
ReplyDeleteYou are very welcome :). The link you posted is nice indeed!!!
ReplyDeleteYou should have a look on the post http://android4developers.blogspot.com.br/2011/02/updating-ui-and-different-threads.html which explain how to do async tasks in Android without AsyncTask. This is very useful for android versions where AsyncTasks were not available.
ReplyDelete