How to use a progress bar from an asynctask

satya - 11/8/2013 11:19:09 AM

Here is the API ref for progressbar

Here is the API ref for progressbar

satya - 11/8/2013 11:20:53 AM

Here are some guidelines from android on showing progress

Here are some guidelines from android on showing progress

satya - 11/8/2013 11:27:38 AM

Example 1

satya - 11/8/2013 11:28:23 AM

Example 2

satya - 11/8/2013 11:29:16 AM

Activity Example1

satya - 11/8/2013 11:29:39 AM

Activity example 2

satya - 11/10/2013 11:46:11 AM

Here is an activity with multiple bars

satya - 11/10/2013 11:47:31 AM

The activity layout that produces this is


<?xml version="1.0" encoding="utf-8"?>
<!-- 
*********************************************
* test_progressbar_activity_layout.xml
* prefix: tpb_
* Test a number of progress bars
* Controls: 
*    A series of progress bars
*    followed by a text view 
*********************************************
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >

    <!--  Regular Progress bar: a rotating circle -->
   <TextView  
       android:layout_width="fill_parent"  android:layout_height="wrap_content" 
       android:text="Regular progress bar" android:layout_marginTop="10dp"
    />
    <ProgressBar
        android:id="@+id/tpb_progressBar1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:background="@android:color/background_light"/>
    
    <!--  Horizontal indefinite Progress bar: a line -->
    <TextView  
       android:layout_width="fill_parent"  android:layout_height="wrap_content" 
       android:text="Indeterminate progress bar" android:layout_marginTop="10dp"
    />
    <ProgressBar
        android:id="@+id/tpb_progressBar3"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:indeterminate="true"
        />
    
     <!--  Horizontal Progress bar: a line -->
    <TextView  
       android:layout_width="fill_parent"  android:layout_height="wrap_content" 
       android:text="Line Progress bar" android:layout_marginTop="10dp"
    />
    <ProgressBar
        android:id="@+id/tpb_progressBar3"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:indeterminate="false"
        android:max="50"
        android:progress="10"
        />   
    <!--  Small spinning circle -->
    <TextView  
       android:layout_width="fill_parent"  android:layout_height="wrap_content" 
       android:text="Small progress bar" android:layout_marginTop="10dp"
    />
    <ProgressBar
        android:id="@+id/tpb_progressBar4"
        style="?android:attr/progressBarStyleSmall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        android:background="@android:color/background_light"/>
    
<TextView  
   android:id="@+id/text1"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Your Debug Will Appear here" android:layout_marginTop="10dp"
    android:background="@drawable/shape_5"
    />
</LinearLayout>

Use this with caution as this is done in a hurry and very early...

satya - 11/10/2013 12:00:22 PM

Here are some other styles


Widget.ProgressBar.Horizontal
Widget.ProgressBar.Small
Widget.ProgressBar.Large
Widget.ProgressBar.Inverse
Widget.ProgressBar.Small.Inverse
Widget.ProgressBar.Large.Inverse

satya - 11/12/2013 12:21:03 PM

In android saving view state if the visibility of the view is GONE

In android saving view state if the visibility of the view is GONE

Search for: In android saving view state if the visibility of the view is GONE

annonymous - 11/15/2013 12:18:02 PM

Progressbar doesn't behave right when the android device is flipped

First of all it must be visible to remember its state on flip (it appears. I need to do more research)

secondly, after flipping although its progress is much higher (getProgress) it seem to show the progress indicator all the way at the beginning visually although setProgress explicitly sets to a fixed later value...This gives the impression that the progress is say at "2" when it is at say "15"

Of course I could be doing something wrong, I will check.

annonymous - 11/15/2013 12:33:44 PM

android progressbar bug on configuration change

android progressbar bug on configuration change

Search for: android progressbar bug on configuration change

annonymous - 11/15/2013 12:53:26 PM

It appears it doesnt remember the max on the orientation change..

It appears it doesnt remember the max on the orientation change..

satya - 11/16/2013 11:08:45 AM

Initial conditions of a ProgressBar view, such as the Max, doesnt seem to be part of the state

This means when the progressbar is recreated, the view only remembers the current progress amount and not the max amount that was set with the progress bar. Just a hunch based on my cursory observations, check for yourself either through experiment or look at the source code of ProgressBar perhaps.

satya - 11/16/2013 11:11:33 AM

Two key things with respect to progress bar and config change

A view may have to be in visible state after the onCreate() to be able to be called to restore its remembered state

A view, like the progress bar, may have its own predilections as to what values constitute its on going state.

A view, like a textview, may even need additional parameters to guide its state management.

satya - 11/16/2013 3:33:14 PM

Ultimately I took over the state management myself in the asynctask

This code below also works as a sample of how one can encapsulate UI and work of the asynctask into one class as much as possible.

It also has a list test cases one should run to validate the work. It is not trivial.


/*
 * To test an asynctask with an embedded progress bar
 * Uses retained fragments as a solution to get to the activity
 * 
 * Key Approach:
 * 
 * 1. Use a retained fragment to connect the activity to the async task
 * 2. use a progressbar to show the progress
 * 3. Control the progressbar as much as possible from the asynctask
 * 4. Takeover the view state of the progressbar here
 * 5. Because the activity is not doing a good job progressbar state
 * 6. Remove the asynctask from the retained fragment once the task
 *    finishes.
 * 7. See MyLongTaskWithFragments to see how the samething is
 *    done using dialogs instead of progressbars
 * 8. Cancels the task on an activity back  
 * 
 * Key testcases:
 * ****************************
 * 1. Regular orientation (Do it 3 times) (success)
 * 2. Flip in the middle (success)
 * 3. Flip and back (success)
 * 4. home and back during
 * 5. home and back after finishing
 * 6. Phone call hiding the activity
 * 7. prevent a back during the task Or cancel task on back 
 * 9. Go back before the task
 * 10. Go back during the task
 * 11. Go back after the task
 * 
 * Key secondary test cases
 * **************************************
 * 1. Cleanup the self pointer (success)
 * 2  Vestigial Testing: Retest the menu (Subsequent invocations may show vestiges)
 * 3. Flip back after finish a few times back and forth (Success)
 *    
 * 1. Regular orientation (Do it 3 times)
 * **************************************
 * See the progress bar
 * See the progress
 * Finish the progress
 * Regain the space
 * Asynctask pointer removed
 * No exceptions in log file
 * 
 * Redo it 3 times
 * 
 * 2. Flip in the middle
 * ***************************************
 * See the progress bar
 * See the progress
 * Flip
 * Pick up the progress where left off
 * Finish the progress
 * Regain the space
 * Asynctask pointer removed
 * No exceptions in log file
 * 
 * Rerun in the new orientation
 * Finish well
 * Regain space
 * No exceptions
 *
 * 3. Flip and back
 * *******************
 * See the progress bar
 * See the progress
 * Flip
 * Pick up the progress where left off
 * Flip again
 * Pick up the progress where left off
 * Finish in the original orientation
 * 
 * Asynctask pointer removed
 * No exceptions in log file
 * 
 * 4. home and back during
 * ***************************
 * See the progress bar
 * See the progress
 * Go home
 * async should be proceeding
 * progress should be proceeding in the background
 * Revisit the app
 * Pick up the progress where left off
 * Finish the progress
 * Regain the space
 * Asynctask pointer removed
 * No exceptions in log file
 * Retest in the same orientation if it works still
 *   
 * 5. home and back after finishing
 * *************************************
 * See the progress bar
 * See the progress
 * Go home
 * async should be proceeding
 * progress should be proceeding in the background
 * wait long enough for the task to finish
 * 2) possibilities
 * 1 is finishes and removes itself
 * 2 is finishes but waits for the restart
 * 
 * I think I used approach 2
 * 
 * Revisit the activtity
 * bar closed
 * async pointer reclaimed
 * 
 * 9. Go back before the task
 * ***************************** 
 * Go back without invoking the activity
 * Make sure nothing is broken
 * Check debug messages
 * we do this because the plumbing may effect initial conditions
 * eventhough the task hasn't started.
 * 
 * 10. Go back during the task
 * ***************************** 
 * See the progress bar
 * See the progress
 * Go back
 * Should see in debug that the task is cancelled
 * The task pointer is freed
 * You can revisit the activity
 * reinvoke the task
 * Complete task successfully
 * 
 * 11. Go back during the task
 * ***************************** 
 * See the progress bar
 * See the progress
 * wait the progressbar to close
 * The task pointer is freed
 * Go back
 * Should see in debug that the right close messages are observed
 * You can revisit the activity
 * reinvoke the task
 * Complete task successfully
 * 
 */
public class MyLongTaskWithProgressBar 
extends AsyncTask<String,Integer,Integer>
implements IWorkerObject
{
   public String tag = null;
   public static String PROGRESS_DIALOG_FRAGMENT_TAG_NAME="AsyncDialog";

   private MonitoredFragment retainedFragment;
   
   //To report back errors
   //Represents the BaseTester
   //This should track the activity status
   //and likely a RADO itself
   private IReportBack r;
   
   //To track current progress
   int curProgress = 0;
   
   MyLongTaskWithProgressBar(MonitoredFragment parentFragment
         ,IReportBack inr
         , String inTag)
   {
      tag = inTag;
      retainedFragment = parentFragment;
      r = inr;
   }
   
   //Gets executed in response to task.execute()
   protected void onPreExecute()
   {
      //Runs on the main ui thread
      Utils.logThreadSignature(this.tag);
      //show the dialog
      showProgressBar();
   }

   /*
    * I am going to start the asynctask
    * show the progress bar
    * we assume the activity is available here
    */
   private void showProgressBar()
   {
      Activity act = retainedFragment.getActivity();
      //assume activity is available
      ProgressBar pb = (ProgressBar) act.findViewById(R.id.tpb_progressBar1);
      pb.setProgress(0);
      pb.setMax(15);
      pb.setVisibility(View.VISIBLE);
   }
   
   
   //this can be null if the activity is not available
   private ProgressBar getProgressBar()
   {
      Activity act = retainedFragment.getActivity();
      if (act == null)
      {
         Log.d(tag, "activity is null. shouldnt be. returning a null dialog");
         return null;
      }
      //Good dialog
      return (ProgressBar) act.findViewById(R.id.tpb_progressBar1);
   }
   
    protected void onProgressUpdate(Integer... progress) 
    {
      //Runs on the main ui thread
      Utils.logThreadSignature(this.tag);
      this.reportThreadSignature();
      
      //will be called multiple times
      //triggered by publishProgress
       Integer i = progress[0];
       
       //Because this is called on the main thread
       //The following check may not be needed
       //if the activity is being re created
       //I could assume this method is delayed
       //until the activity is ready to receive
       //its messages.
       if (retainedFragment.isUIReady())
       {
          r.reportBack(tag, "Progress:" + i.toString());
          //set progress
          //wpd.get().setProgress(i);
          setProgressOnProgressBar(i);
       }
       else
       {
          Log.d(tag,"Activity is not ready! Progress update not displayed:" + i);
       }
    }

    private void setProgressOnProgressBar(int i)
    {
       this.curProgress = i;
       ProgressBar pbar = getProgressBar();
       if (pbar == null)
       {
          Log.d(tag, "Activity is not available to set progress");
          return;
       }
       r.reportBack(tag, "pbar:" + pbar.getProgress());
       pbar.setProgress(i);
       
    }
    private void closeProgressBar()
    {
       ProgressBar pbar = getProgressBar();
       if (pbar == null)
       {
          Log.d(tag, "Sorry progress bar is null to close it!");
          return;
       }
       //Dismiss the dialog
       Log.d(tag,"Progress bar is being dismissed");
       pbar.setVisibility(View.GONE);
       detachFromParent();
    }
    
    //when you start say you are not done
    //Mark it done on postExecute
    private boolean bDoneFlag = false;
    protected void onPostExecute(Integer result) 
    {         
      //Rember it to fix it when coming back up
      bDoneFlag = true;
      
      //Runs on the main ui thread
      Utils.logThreadSignature(this.tag);
      conditionalReportBack("onPostExecute result:" + result);
      
      //we need to close the dialog. However....
      //closeProgressDialog()
      
      //Conditions I ahve to take into account
      //1. activity not there
      //2. Activity is there but stopped
      //3. Activity is UI ready
      if (retainedFragment.isUIReady())
      {
         Log.d(tag,"UI is ready and running. dismiss is pemissible");
         closeProgressBar();
         return;
      }
      //either not there or stopped
      Log.d(tag,"UI is not ready. Do this on start again");
    }
    protected Integer doInBackground(String...strings)
    {
      //Runs on a worker thread
       //May even be a pool if there are 
       //more tasks.
      Utils.logThreadSignature(this.tag);
      
       for(String s :strings)
       {
          Log.d(tag, "Processing:" + s);
          //r.reportTransient(tag, "Processing:" + s);
       }
       for (int i=0;i<15;i++)
       {
          Utils.sleepForInSecs(2);
          publishProgress(i);
       }
       return 1;
    }
    protected void reportThreadSignature()
    {
       String s = Utils.getThreadSignature();
         conditionalReportBack(s);
    }
    private void conditionalReportBack(String message)
    {
       Log.d(tag,message);
       r.reportBack(tag,message);
    }
    
    /**
     * Call this from your onStart
     * @param act
     */
   public void onStart(Activity act) {
      
      //dismiss dialog if needed
      if (bDoneFlag == true)
      {
         Log.d(tag,"On my start I notice I was done earlier");
         closeProgressBar();
         return;
      }
      Log.d(tag,"I am reattached. I am not done");
      setProgressBarRightOnReattach();
   }
   
   private void setProgressBarRightOnReattach()
   {
      ProgressBar pb = getProgressBar();
      pb.setMax(15);
      pb.setProgress(curProgress);
      pb.setVisibility(View.VISIBLE);
   }
    
    
   private void detachFromParent()
   {
      if (client == null)
      {
         Log.e(tag,"You have failed to register a client.");
         return;
      }
      //client is available
      client.done(this,workerObjectPassbackIdentifier);
   }
   
   //To tell the called that I have finished
   IWorkerObjectClient client = null;
   int workerObjectPassbackIdentifier = -1;
   
   public void registerClient(IWorkerObjectClient woc,
         int inWorkerObjectPassbackIdentifier) {
      Log.d(tag,"Registering a client for the asynctask");
      client = woc;
      this.workerObjectPassbackIdentifier = inWorkerObjectPassbackIdentifier;
   }
   
   public void releaseResources()
   {
      //Cancel the task
      Log.d(tag,"Cancelling the task");
      cancel(true);
      //Remove myself
      detachFromParent();
   }

}//eof-class