Tuesday 10 June 2014

AsyncTask Framework With Example

AsyncTask Framework

When an application is launched, the android system create a thread of execution for the application. This thread is called as Main Thread. All the components (Activity, services, content provider and broadcastreceiver) run on this single thread only. For better responsiveness and time consuming processes, we often need a separate thread to perform these kind of operations. Some of the operation like Network Operation are not allowed on this main thread of the application.

Android provides various frameworks and API to overcome these scenario. Use of Handler and Runnable is one of them other is AsyncTask framework. In this post we will go through the AsyncTask framework, its benefits, use cases and drawbacks also.

AsyncTask enables proper and easy use of the UI thread. This class allows performing background operations and publishing results on the UI thread without having to manipulate threads and/or handlers. Applications can customize AsyncTask to meet their concurrency needs. A detailed framework overview you can get on official page of AsyncTask of android website.

Features of AsyncTask

It provides a very simple solution to perform some operation on a separate background thread. This background thread is created by the framework itself, you don’t need to create separately. It also provides a easier to communicate with the UI main thread to this background thread. Results produced by the framework is used to publish them on the UI thread. The operation performed by the AsyncTask is done on the background thread inside the doInBackground method of this class. This is the the only method which runs inside the background thread. All other methods of this class runs on the caller thread i.e main thread. AsyncTask should be used to perform some action which is of a short duration, preferably less than 5 seconds.  If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

This is a very tightly coupled framework unlike the Handler and Runnable framework, which is loosely coupled. In this framework you just have to extends this class and override the required methods, thats all. There is no need to manipulate any thread, Handler and Runnable objects.

AsyncTask Implementation

To implement AsyncTask you have to extend this class and pass the parameterized parameter for input, progress and output. You have override at least one method of this class which is doInBackground method. It is optional to override the other methods. If you have to use the result to update the UI view then also override the onPostExecute method. Below is the skeleton code for a custom AsyncTask implementation.

public class DownloadAsyncTask extends AsyncTask<String,Integer,String>{

// Do some initialization here if required
// Called before doInBackground
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
// only method to run on background thread
// Do the background job here only
@Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
publishProgress(10);
return null;
}
// Called each time when publishProgress is
// invoked from doInBackground
                     // show progress bar here
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
}
// Called after doInBackground finishes its job
// update UI element here only
                     // Dismiss progress bar here if any
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}
// Called When caller invoke cancel method
// while AsynTask still inside doInBackground
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
}
// Called when cancel(boolean) is caller by
// caller and doInBackground is finishes its job
@Override
protected void onCancelled(String result) {
// TODO Auto-generated method stub
super.onCancelled(result);
}
}

How to use AsyncTask
Once you created the custom AsyncTask class you want to use it inside the UI main thread. First create an object of this class and invoke the execute method of this class. You have to pass the input parameter inside the execute method if it is required. In below code snippet a url String is passed to this method.

String url = “http://………….../”;
AsyncTask downloadTask = new DownloadAsyncTask();
downloadTask.execute(url);

AsyncTask 3 Generic Types

class DownloadAsyncTask extends AsyncTask<Params, Progress, Result> {
.
.
}

AsyncTask class is a parameterized class that need three generic parameters called as Params, Progress and Result. These parameters are used inside the methods of the class as inputs for them.

Params is Input parameter for the for AsyncTask. This can be passed from the public method execute of the class. You can pass nothing at all inside the execute as well. In this case Param should be provided to the class as Void. Similarly, for progress and Result inputs can be given. Progress is used for the user to indicate the progress of the task. This is generally shown using a progress bar. Result is produced by the doInBAckground on its completion and used by the onPostExecute method.

All these three parameter can be of any class level variables like String, Integer, Custom class etc. You can not use basic primitive type here. If you do not want to use any parameter for any of these, then use class Void for them.

Methods inside AsyncTask and their key characteristics

Public method
(Used By Application)
Protected Methods
(Used By Framework)
execute( Params … params)
execute(Runnable)
cancel(boolean)
isCancelled()
doInBackground(Params… params)
postExecute(Result)
progressUpdate(Progress…)
publishProgress(Progress …)
onPreExecute ()
onCancelled()

Method used inside this class are shown in the above table. These methods are public methods and protected methods. The public methods are executed by the caller and protected methods are called by the system.

The execute method can be called only once per instance of the AsyncTask by the caller. The onProgressUpdate is called each time whenever publishProgress is called from the doInBackground method. If there a loop inside the doInBackground method and publishProgress is called inside that loop then onProgressUpdate will also be called by the system for each iteration of the loop. You should check isCancelled method from the doInBackground method whether the task is cancelled by the caller in the meantime or not.
If it cancelled then come out of this method using break statement. All protected method except doInBackground are run on the caller (ex- UI) thread only not on the background thread.

Cancelling a task
A running asyncTask can be cancelled at any time by the caller by invoking the public cancel method of the class. This triggers the protected method onCancelled by the system. If doInBackground is running at the time of cancellation, it will stop further execution and invoke the onCancelled method. If it has finishes its task on the background thread then the protected method onCancelled (Result) will be invoked by the system. It is a good practise to ensure that a task is cancelled or not by checking the return value of isCancelled periodically inside the doInBackground method.

Drawback of AsyncTask
The task should be handled carefully whenever there is a recreation of the activity class for example on rotation of the device. Since activity is recreated but the task is still running in the background and may be killed although onDestroy method of the activity is called by the system at that time. So upon recreation of activity there might be a possibility that another instance of the task starts running and now there are two instances of the task are trying to achieve the same goal and this will create a problem. You should use service and headless fragment in these scenario.

A Simple Example of AsyncTask
This is a simple example of usage of AsyncTask framework. In this example I have created two async tasks and executed both of them using button. This also illustrate the two threads are running in parallel along with UI thread. One task is download task and other task is simply providing a message to UI thread. A cancel button is provided there which can be used to cancel the task.


MainActivity.java

package pp.asynctasktest;

import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends Activity {

      Button btxt ;
      Button bcancel;
      ProgressBar bartxt ;
      TextView tv ;      
      String settxt=null;
        
      Button bdownload;
      ImageView iv ;
      ProgressDialog barimg;
        
         TextAsyncTask changeText;
      ImageAsyncTask downloadImage;
        
      @Override
      protected void onCreate(Bundle savedInstanceState) {
               super.onCreate(savedInstanceState);
               setContentView(R.layout.activity_main);

               btxt = (Button)findViewById(R.id.button1);
               bcancel=(Button)findViewById(R.id.button2);
               bartxt = (ProgressBar)findViewById(R.id.progressBar1);
               tv = (TextView)findViewById(R.id.textView1);
                 
               bdownload=(Button)findViewById(R.id.button3);
               iv = (ImageView)findViewById(R.id.imageView1);
                 
               btxt.setOnClickListener(new View.OnClickListener() {                    
                         @Override
                         public void onClick(View v) {
                                  // TODO Auto-generated method stub
                                     changeText = new TextAsyncTask();
                                  changeText.execute();
                         }
               });
                 
               bcancel.setOnClickListener(new View.OnClickListener() {                            
                         @Override
                         public void onClick(View v) {
                                  // TODO Auto-generated method stub
                                     changeText.cancel(true);
                         }
               });
                 
               bdownload.setOnClickListener(new View.OnClickListener() {
                           
                         String url1="http://www.tuxpaint.org/stamps/stamps/animals/birds/cuckoo.png";
                         //String url2="http://www.tuxpaint.org/stamps/stamps/animals/birds/adelaide-rosella.png";
                         @Override
                         public void onClick(View v) {                                     
                                  // TODO Auto-generated method stub
                                     downloadImage = new ImageAsyncTask();
                                  downloadImage.execute(url1);
                         }
               });
      }

         private class TextAsyncTask extends AsyncTask<Void, Integer, Void>{

               @Override
               protected void onPreExecute(){
                         Toast.makeText(getApplicationContext(), "Inside onPreExecute",Toast.LENGTH_SHORT).show();
               }
                 
                  @Override
               protected Void doInBackground(Void... arg0) {
                         // TODO Auto-generated method stub
                         for (int i = 0; i <= 100; i++)
                         {
                                  final int value = i;
                                  try {
                                            Thread.sleep(100);
                                  } catch (InterruptedException e) {
                                            e.printStackTrace();
                                  }
                               publishProgress(value);                                                                                                                                                        
                         }
                                                                                                        
                         settxt="Set By AsynTask";
                         return null;
               }
                 
               @Override
                  protected void onProgressUpdate(Integer... progress){
                         bartxt.setProgress(progress[0]);
               }
                 
               @Override
                  protected void onPostExecute(Void v){
                         tv.setText(settxt);
               }
                 
               @Override
                  protected void onCancelled(){
                         Toast.makeText(getApplicationContext(), "Thread is cancelled",Toast.LENGTH_SHORT).show();
               }
      }
        
         private class ImageAsyncTask extends AsyncTask<String, Integer, Bitmap>{
                           
               @Override
                  protected void onPreExecute(){
                         barimg=ProgressDialog.show(MainActivity.this, "Download Image", "Loading...");
                         //Toast.makeText(getApplicationContext(), "Inside onPreExecute",Toast.LENGTH_SHORT).show();
               }
                 
               @Override
                  protected Bitmap doInBackground(String... url) {
                         // TODO Auto-generated method stub
                         final DefaultHttpClient client = new DefaultHttpClient();                                         
               final HttpGet getRequest = new HttpGet(url[0]);
           try {
                    HttpResponse response = client.execute(getRequest);
                    //check 200 OK for success
                    final int statusCode = response.getStatusLine().getStatusCode();  
                    if (statusCode != HttpStatus.SC_OK) {
                           Log.w("ImageDownloader", "Error " + statusCode +
                                   " while retrieving bitmap from " + url);
                           return null;
                    }
                    final HttpEntity entity = response.getEntity();
                    if (entity != null) {
                  InputStream inputStream = null;
                  try {                     
                      inputStream = entity.getContent();                              
                      Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                      return bitmap;
                  } finally {
                      if (inputStream != null) {
                          inputStream.close();
                      }
                      entity.consumeContent();
                  }
              }
          } catch (Exception e) {
              getRequest.abort();
              Log.e(getString(R.string.app_name), "Error "+ e.toString());
          }
     
          return null;
               }
                 
               @Override
               protected void onProgressUpdate(Integer... progress){
                           
               }
                 
               @Override
                  protected void onPostExecute(Bitmap bm){
                         barimg.dismiss();
                         iv.setImageBitmap(bm);
               }
                 
               @Override
               protected void onCancelled(){
                         Toast.makeText(getApplicationContext(), "Thread is cancelled",Toast.LENGTH_SHORT).show();
                         Toast.makeText(getApplicationContext(), "Image is not downloaded",Toast.LENGTH_SHORT).show();
               }

                 
      }
}

Activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/container"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context="pp.asynctasktest.MainActivity"
   tools:ignore="MergeRootFrame" >

   <RelativeLayout
       android:id="@+id/async_text"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:background="#222222">

       <Button
           android:id="@+id/button1"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_alignParentLeft="true"
           android:layout_below="@+id/progressBar1"
           android:layout_marginTop="70dp"
           android:text="@string/button_txt" />

       <TextView
           android:id="@+id/textView1"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_alignParentLeft="true"
           android:layout_alignParentRight="true"
           android:layout_below="@+id/progressBar1"
           android:layout_marginTop="28dp"
           android:text="@string/update_text"
           android:textAppearance="?android:attr/textAppearanceMedium"
           android:textColor="#ff0000"
           android:background="#fafaaa"/>

       <ProgressBar
           android:id="@+id/progressBar1"
           style="?android:attr/progressBarStyleHorizontal"
           android:layout_width="209dp"
           android:layout_height="wrap_content"
           android:layout_alignParentLeft="true"
           android:layout_alignParentRight="true"
           android:layout_alignParentTop="true"
           android:layout_marginTop="20dp" />

       <Button
           android:id="@+id/button2"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_alignBottom="@+id/button1"
           android:layout_alignParentRight="true"
           android:text="@string/button_cancel" />
   </RelativeLayout>

   <RelativeLayout
       android:id="@+id/async_download"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentBottom="true"
       android:layout_alignParentLeft="true"
       android:layout_alignParentRight="true"
       android:layout_below="@+id/async_text"
       android:orientation="vertical"
       android:layout_marginTop="12dp"
       android:background="#fff99f">

       <ImageView
           android:id="@+id/imageView1"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:layout_below="@+id/button3"
           android:layout_centerHorizontal="true"
           android:contentDescription="@string/download_image"          
           android:src="@drawable/ic_launcher" />

       <Button
           android:id="@+id/button3"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:layout_alignParentLeft="true"
           android:layout_alignParentTop="true"
           android:text="@string/button_download"
           android:layout_marginTop="3dp" />

   </RelativeLayout>

</RelativeLayout>

res/value/string.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

   <string name="app_name">AsyncTaskTest</string>
   <string name="hello_world">Hello world!</string>
   <string name="action_settings">Settings</string>   
   <string name="button_txt">Update Text</string>
   <string name="update_text">Updated By Async Task</string>
   <string name="button_cancel">Cancel Text Updation</string>
   <string name="button_download">Download Image</string>
   <string name="download_image">Downloaded Image</string>
      
</resources>


1 comment:

  1. I understood the concepts of AsyncTask very well.....Thank U !!

    ReplyDelete