Friday, November 9, 2012

Basic AsyncTask with a progress bar widget

After a few days far from this coding corner, today I'm going to show you an example with one important tool for executing tasks in background with Android. In earlier versions of Android, background execution of task was made managing threads and handlers, as you can see in this example. It was not difficult, but threads are always a thorny issue. Google has simplified this mechanism by means of AsyncTask class.

In this AsyncTask tutorial I have implemented a progress bar widget in the main activity. You can set the number of seconds the task will take. It's a very simple example. Let's go!!!!

First we will define the progress bar in the main.xml file, setting it as invisible in the visibility property:

<ProgressBar
    android:id="@+id/pbDefault"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    style="@android:style/Widget.ProgressBar.Horizontal"
    android:visibility="invisible"
 />

After that, we will define ProgressBarAsyncTask, which will be in charge of doing a background work and update its progress in the progress bar widget. Methods are explained with comments over the code:

import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;

/**
 * ProgressBarAsyncTask extends from AsyncTask class. It is a template that 
 * is defined as follows:
 * 
 *      AsyncTask< InitialTaskParamsType, ProgressType, ResultTaskType>      
 *      
 */
public class ProgressBarAsyncTask extends AsyncTask {

   private static final String LOG_TAG = "PB_ASYNC_TASK";
   private ProgressBar pbM;
   private EditText teSecondsProgressedM;
   
   /**
    * The parameters in the constructor of the class are the controls from the
    * main activity that we will update as the background work is progressing.
    *  
    * @param pb: the progress bar control.
    * @param secondProgressed: an edit text with the percentage of seconds 
    *                          progressed.
    */
    public ProgressBarAsyncTask(ProgressBar pb, EditText secondProgressed) {
      Log.d(LOG_TAG, "Constructor");
  
      pbM = pb;
      teSecondsProgressedM = secondProgressed;
    }

    /**
     * This method will be called before the execution of the task. Here we 
     * are activating the visibility of the progress controls of the main
     * activity.
     */
    @Override
    protected void onPreExecute() {
      Log.d(LOG_TAG, "Pre-Execute");

      super.onPreExecute();
      pbM.setVisibility( View.VISIBLE);
      teSecondsProgressedM.setVisibility( View.VISIBLE);
    }

    /**
     * This method will be called after the invocation of the 
     * publishProgress( progress) method in the background task. Here is where
     * we update the progress controls.
     * 
     * @param progress: the amount of progress of the background task
     */
    @Override
    protected void onProgressUpdate(Integer... progress) {
      Log.d(LOG_TAG, "Progress Update: " + progress[0].toString());

      super.onProgressUpdate( progress[0]);  
      pbM.setProgress( progress[0]);
      teSecondsProgressedM.setText( progress[0].toString());
    }
 
    /**
     * This method is called after the execution of the background task. Here
     * we reset the progress controls and set their visible property to 
     * invisible again, to hide them.
     * 
     * @param result: is the result of the background task, and it is passed to
     *                this method with de result returned in the 
     *                doInBackGroundMethod()
     */
    @Override
    protected void onPostExecute(Boolean result) {
       Log.d(LOG_TAG, "Post-Execute: " + result);
  
       super.onPostUpdate( result);
       pbM.setVisibility( View.INVISIBLE);
       teSecondsProgressedM.setVisibility( View.INVISIBLE);
       teSecondsProgressedM.setText("");
       pbM.setProgress(0);
    }

    /**
     * This method is called for executing the background task in the AsyncTask.
     * For this tutorial we are only sleeping the thread for the number of 
     * seconds passed as parameter of the function.
     * 
     * @param numSeconds: life of the task
     * @return the result of the background task
     */
    @Override
    protected Boolean doInBackground(Integer... numSeconds) {
      Log.d(LOG_TAG, "doInBackground: " + numSeconds[0]);
  
      try {  
        int totalSecs = numSeconds[0].intValue();
        Log.d(LOG_TAG, "Total SECS: " + totalSecs);
   
        for (int i = 1; i <= totalSecs; i++) {
           Log.d(LOG_TAG, "Sleeping " + i);
           Thread.sleep(1000);
    
           float percentage = ((float)i / (float)totalSecs) * 100;
           Log.d(LOG_TAG, "Percentage of progress: " + percentage);
    
           publishProgress( new Float( percentage).intValue());
        }  
      } catch (InterruptedException e) {
          e.printStackTrace();
          return false;
      }

      return true;
   }
}
Now its the time for implementing the main activity: 
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;

public class ProgressBarExampleActivity extends Activity {
 
   private static final String LOG_TAG = "PB_EXAMPLE";
   private EditText etNumSecondsM;
   private EditText etSecondsProgressedM;
 
   private Button bExitM;
   private Button bExecuteM;
   private ProgressBar pbDefaultM;
    
   /** Called when the activity is first created. */
   @Override
   public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
     drawGUI();
   }
    
   public void drawGUI()
   {
      Log.d(LOG_TAG, "Creating Graphic Interface");
      setContentView(R.layout.main);
        
      //Text Editors
      etNumSecondsM = (EditText) findViewById(R.id.etNumSeconds);
      etSecondsProgressedM = (EditText) findViewById( R.id.etSecondProgressed);
        
      //Buttons
      bExecuteM = (Button) findViewById(R.id.bExecute); 
      bExitM = (Button) findViewById(R.id.bExit);
        
      //Progress Bar
      pbDefaultM = (ProgressBar) findViewById( R.id.pbDefault);    
        
      // When the execution button is pressed, the AsyncTask is created and
      // executed.
      bExecuteM.setOnClickListener(new View.OnClickListener() {
         public void onClick(View view) {
            ProgressBarAsyncTask pbTask = new ProgressBarAsyncTask( pbDefaultM,
                                                                    etSecondsProgressedM);
            pbTask.execute( new Integer( etNumSecondsM.getText().toString()));
         }
      });

      bExitM.setOnClickListener(new View.OnClickListener() {
         public void onClick(View view) {
             exit( RESULT_OK);
         }
      });  
    }
    
    public void exit( int theResult)
    {
       setResult( theResult);
       finish();
    }
}
Here the clue is the creation of the ProgressBarAsyncTask and its execution with execute()method, when the "Show Progress Bar" button is pressed. I hope this example is interesting for you. I will return soon with a tutorial for canceling the AsyncTask. See you soon.

No comments:

Post a Comment