Eigo Logo Follow us on Twitter

Threads and Progress Dialogs in Android Screen Orientation Rotations

 
Martin Belcher 13th May 2010

Part 2 of 2Android progress bar image

Intro

This article continues the discussion of using threads in Android activities started in the previous article; Programming threaded processes in Android.

This article describes how to show a progress dialog and keep a thread and progress dialog working across screen rotation on Android.

Using a Progress Dialog in the UI

A progress dialog is part of the Android API and can be used to display either a progress bar or a spinning progress symbol. Dialogs are shown by handling the activity’s onCreateDialog(int) event , there is a handy tutorial on dialogs on the Android developer website.

Continuing using our activity class from the last article, first thing we need to do is add a private constant to use to identify the dialog and a private field to keep a reference to the dialog.

[Code sample – constant and variable declarations]
// ***************************
// Private constants
// ***************************
private static final int DIALOG_PROGRESS = 1;

// ***************************
// Private variables
// ***************************
// Progress dialog for when posting to servers
private ProgressDialog moProgressDialog = null;

Secondly in the onClick handler just before we start the worker thread add some code to show the dialog.

[Code sample – how to show a dialog]
// Show the image dialog
showDialog(DIALOG_PROGRESS);

Finally add a handler for the onCreateDialog(int) event of the activity.

[Code sample – onCreateDialog event handler]
// Handle the event for creation of dialogs
@Override
protected Dialog onCreateDialog(int id
{
 switch(id
 {
  case DIALOG_PROGRESS:
   moProgressDialog = new ProgressDialog(MyMainUiActivity.this);
   moProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
   moProgressDialog.setTitle("Please Wait");
   moProgressDialog.setMessage("Worker process is busy");
   return moProgressDialog;
  default:
   return null;
 }
}

This code actually creates the dialog instance.

Updating the Progress Dialog

Now that the progress dialog exists and is displayed we need to update it with the percentage of progress made so far in our worker thread and the close the dialog once our work is finished.

In the handleMessage() function when the message returned is MESSAGE_PROGRESS, add the following code to set the position of the progress bar to the percentage of progress so far.

[Code sample – update progress bar of dialog]
// Update the progress bar
moProgressDialog.setProgress(oMessage.arg1);

When the message returned is anything else, for example complete or error, the following code closes the dialog.

[Code sample – close progress dialog]
// Close the progress dialog 
dismissDialog(DIALOG_PROGRESS);

The new handler code looks like this:

[Code sample – thread event handler]
// Define the Handler that receives messages 
// from the thread and update the progress
// Event handler for this Activity
final Handler moHandlerOfThis = new Handler() 
{
    public void handleMessage(Message oMessage
    {
      TextView txtOutput = 
        (TextView)findViewById(R.id.txtOutput);
          
        // Decide what to do 
      switch (oMessage.what)
      {
        case MyThreadClass.MESSAGE_COMPLETE:
          // Check the value returned
          txtOutput.setText("Process complete, result = " 
              + String.valueOf(oMessage.arg1));
          moMyThreadClass = null;
          // Close the progress dialog 
          dismissDialog(DIALOG_PROGRESS);
          break;
        case MyThreadClass.MESSAGE_ERROR:
          // Show the error message 
          txtOutput.setText("Process failed, error = " 
              + oMessage.getData().getString("Message"));
          // Close the progress dialog 
          dismissDialog(DIALOG_PROGRESS);
          // Get rid of the sending thread
          moMyThreadClass.StopThread();
          moMyThreadClass = null;
          break;
        case MyThreadClass.MESSAGE_PROGRESS:
          // Update the progress bar
          moProgressDialog.setProgress(oMessage.arg1);
          break;
          
        default:
          // This should never occur
          txtOutput.setText("Unknown message type = " 
              + String.valueOf(oMessage.what));
          // Close the progress dialog 
          dismissDialog(DIALOG_PROGRESS);
          // Get rid of the sending thread
          moMyThreadClass.StopThread();
          moMyThreadClass = null;
          break;
      }
    }
};

Screenshot of Progress Dialog in use

Screenshot of Progress Dialog in use

Screen Rotation: Activity Lifecycle

When the screen orientation is changed the activity is closed and destroyed then created again. This means that all the variables and references are lost, this is no good if the worker thread is in the middle of a process because the activity will not be able to respond to its events.

To solve this problem a reference the worker thread needs to be kept and any state information from the activity that needs preserving needs to be stored. The kind of state information that you would want to preserve is say the text of a TextView or the checked status of a CheckBox.

There are two common places to store state information:

  1. The saved instance state bundle which can be written to in the handler of the onSaveInstanceState(Bundle) activity event and read out again in the onCreate(Bundle) activity event.
  2. The SharedPreferences for the application by writing to the in the onPause() activity event , or some other event, and reading them out again in the onCreate(Bundle) activity event.

This article will not cover storing state information in any more detail but a future article with code examples might be considered.

Another option is to force the screen not to rotate; the topic is also out of scope for this article but there is another article on this website: Lock Screen Orientation in Android

Screen Rotation: Keeping a Reference to the Thread

As mentioned in the previous section a reference to the worker process thread needs to be kept so that the progress dialog can continue to be updated and the final result acted up in the activity.

This is done by handling the onRetainNonConfigurationInstance() activity event to store the worker thread class instance when the form is destroyed.

[Code sample – how to retain a class instance after activity destruction]
// Store the sending thread between screen rotations
@Override
public Object onRetainNonConfigurationInstance() 
{
  removeDialog(DIALOG_PROGRESS);
  // Check that there is a worker thread that
  // needs preserving
  if (moMyThreadClass != null)
  {
    // remove reference to this activity 
    // (important to avoid memory leak)
    moMyThreadClass.HandlerOfCaller = null;
    // Return the instance to be retained
    return(moMyThreadClass);
  }
  return super.onRetainNonConfigurationInstance();
}

Screen Rotation: Restoring the Thread and Process Dialog

After rotation has changed the screen orientation and destroyed the activity the activity is created again and the thread instance needs to be restored and the progress dialog updated.

To get the thread class back again while the form is reloaded with the new orientation call getLastNonConfigurationInstance() in the onCreate(Bundle) activity event . Check the status of the thread and show/hide the progress dialog accordingly.

[Code sample – how to restore a class instance after activity creation]
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    
    Button oButton = (Button)findViewById(R.id.btnTest);
    oButton.setOnClickListener((OnClickListenerthis);
    

    // Get the last sending thread if it was still going
  if (getLastNonConfigurationInstance() != null)
  {
      TextView txtOutput = 
        (TextView)findViewById(R.id.txtOutput);
    // Check that it is still running
    moMyThreadClass = 
      (MyThreadClass)getLastNonConfigurationInstance();
    moMyThreadClass.HandlerOfCaller = moHandlerOfThis;

    // Check that it is still running
    switch (moMyThreadClass.GetStatus())
    {
    case MyThreadClass.STATE_RUNNING:
      // Show the progress dialog again
      showDialog(DIALOG_PROGRESS);
      break;
    case MyThreadClass.STATE_NOT_STARTED:
      // Close the progress dialog in case it is open
      dismissDialog(DIALOG_PROGRESS);
      break;
    case MyThreadClass.STATE_DONE:
      // Check the value returned
      txtOutput.setText("Process complete, result = " 
          + String.valueOf(moMyThreadClass.Result));
      moMyThreadClass = null;
      // Close the progress dialog in case it is open
      dismissDialog(DIALOG_PROGRESS);
      break;
    default:
      // This should never occur
      txtOutput.setText(
          "Unknown MyThreadClass status type = " 
          + String.valueOf(moMyThreadClass.GetStatus()));
      // Close the progress dialog in case it is open
      dismissDialog(DIALOG_PROGRESS);
      // Get rid of the sending thread
      moMyThreadClass.StopThread();
      moMyThreadClass = null;
      break;
    }
  }
}

It is also important to pass our worker thread the handler of the activity to its HandlerOfCaller field so that it can raise events for the activity to capture during the continuing execution.

 

Add Comment

 
Name:
Email:
This email address will not be shown on the website and will not be distributed to any other parties. By submitting this email address you agree to receive an email from Eigo in the future, that email will include an option to unsubscribe from future emails.
Comment:
(HTML allowed)
Website: (Optional)
A link to show beneath your comment.
Security Check: