Wednesday, December 19, 2012

Using AsyncTask

A very usefull class in Android is the AsyncTask.

It's an easy way to make your application do something in background and bringing the results of this action to the UI. In this post, I'm going to show you how you can use AsyncTask in your application.
I made a simple App that generates a random number between 0 and 10 every second. After 7 times, it returns the sum of these generated values. In the meanwhile, each time a number is generated, the user will be informed about it.

First of all

Before we start the AsyncTask to do its job, we have to give it some parameters to work with.
While working, we want to see the progress of the AsyncTask.
And of course, eventually, we want to have a result.

If we want to create an AsyncTask, we need to specify these 3 things. Our AsyncTask class will extend AsyncTask<Params, Progress, Result>

In this case, we will make a RandomCountingTask.
The parameters we want to give to the task are the number of times it should generate a number and how large this generated number may be. These are both Integers, in this case 10 the maximum value of a number and 7 for the amount of numbers we want.

The Progress in this case is the generated number, so this is also an Integer.

The Result will also be an Integer, the sum of all generated numbers.

So our task has to look like this:

class RandomCountingTask extends AsyncTask<Integer, Integer, Integer>{ }

If we would write this in Eclipse, we would have to add unimplemented methods. This is the method doInBackground(Integer... params){}


AsyncTask 'Lifecycle'

Since the AsyncTask is created to do stuff in background and bring results and progress to the UI, it has methods on the Main Thread (UI) and methods on a single Worker Thread (Background).
This is how it goes:



Code

Now without further ado, here's the code of this simple example:


package com.bonappetit.demos.asynctask;

import java.util.Random;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class AsyncTaskDemo extends Activity implements OnClickListener {

 private TextView txtMain;
 private Button btnStartStop;
 private AsyncTask<Integer, Integer, Integer> countingTask;
 private boolean isCounting;



 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_async_task_demo);
  setUpViews(); 

 }

 private void setUpViews() {
  txtMain = (TextView)findViewById(R.id.txtAsyncTaskDemoMainText);
  btnStartStop = (Button)findViewById(R.id.btnAsyncTaskDemoStartStop);
  btnStartStop.setOnClickListener(this);
  btnStartStop.setText("Start"); 

 }



 @Override
 public void onClick(View v) {
  if(v == btnStartStop){
   if(!isCounting){
    txtMain.setText("");//clear the text
    countingTask = new RandomCountingTask(); // create a new task
    //a task can be executed only once;
    Integer[] values = new Integer[2];
    values[0] = 10; //number between 0 and 10
    values[1] = 7; //generate 7 times
    countingTask.execute(values);
   }else{
    countingTask.cancel(true);
   }
  }  

 }

 public boolean isCounting() {
  return isCounting;
 }

 public void setCounting(boolean isCounting) {
  this.isCounting = isCounting;
 }


 class RandomCountingTask extends AsyncTask<Integer, Integer, Integer>{


  @Override
  protected void onPreExecute() {
   super.onPreExecute();
   Toast.makeText(AsyncTaskDemo.this, "Started counting", Toast.LENGTH_SHORT).show();
   setCounting(true);
   btnStartStop.setText("Stop");
  }

  @Override
  protected Integer doInBackground(Integer... params) {
   int maxValue = params[0];
   int maxTimes = params[1];
   int sum = 0;
   int randomNumber;
   Random r = new Random();
   for(int i = 0; i<maxTimes; i++){
    randomNumber = r.nextInt(maxValue);
    sum += randomNumber;
    publishProgress(randomNumber);   

    try {
     Thread.sleep(1000); //Sleep for 1 second
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   return sum;
  }  

  @Override
  protected void onProgressUpdate(Integer... values) {
   super.onProgressUpdate(values);
   txtMain.append("Added " + values[0] + " to the sum\n");
  }



  @Override
  protected void onPostExecute(Integer result) {
   super.onPostExecute(result);
   setCounting(false);
   Toast.makeText(AsyncTaskDemo.this, "Stopped counting", Toast.LENGTH_SHORT).show();
   txtMain.append("Result is " + result);
  }  

  @Override
  protected void onCancelled() {
   super.onCancelled();
   setCounting(false);
   Toast.makeText(AsyncTaskDemo.this, "Counting was cancelled", Toast.LENGTH_SHORT).show();  
  }
 }

}


Important notes

  • There is a limit of AsyncTasks to use in your application (another post on this will follow)
  • An instance of an AsyncTask can only be executed once.
  • Something like Integer... can be one or more integers