Eigo Logo Follow us on Twitter

Programming threaded processes in Android

 
Martin Belcher 7th May 2010

Part 1 of 2

Intro

After a period of time when executing a process that takes a long time on the UI thread in Android the virtual machine will force the application to close since it is being unresponsive. The solution to this problem is to move your process to a new thread and allow the UI thread to continue running.

This article describes one technique for creating and managing processes in new threads in Android apps.

Creating a Thread

In the technique described in this article we start by creating a new class which inherits (extends) from the java.lang.Thread class.

[Code sample – the basic thread class]
public class MyThreadClass extends Thread 
{

  // **********************************
    // Public methods
    // **********************************
    // This method is run when the thread
  // is started by calling start().
  @Override
    public void run() 
    {
      // Do something time consuming
      for (int i = 0; i < 10; i++)
      {
            try 
            {
        Thread.sleep(1000);
      
            catch (InterruptedException e
      {
        e.printStackTrace();
      }
      }
    }
}

When the thread is started the overridden run() method will be called and its code executed. The thread ends once this method has been executed. The loop and sleep code in the run() method is just to give the thread something to do since this is just an example, in real life this would be replaced with the process that needs running.

To start this thread from the UI activity do the following, perhaps in a click event handler for example.

[Code sample – xml layout for sample UI Activity]

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

<LinearLayout

  xmlns:android="http://schemas.android.com/apk/res/android"

  android:layout_width="fill_parent"

  android:layout_height="fill_parent"

  android:orientation="vertical"

  >

 

  <Button

    android:id="@+id/btnTest"

      android:layout_width="fill_parent"

      android:layout_height="wrap_content"

      android:text="Test"

  />

  <TextView

    android:id="@+id/txtOutput"

      android:layout_width="fill_parent"

      android:layout_height="fill_parent" 

      android:text=""

    />

 

</LinearLayout>



[Code sample – UI Activity code to start the thread]
public class MyMainUiActivity 
extends Activity  implements OnClickListener
{
  // ***************************
    // Private variables
    // ***************************
  // Worker thread class
  private MyThreadClass moMyThreadClass = null;
    // ***************************
    // Activity Lifetime Events
    // ***************************
    /** 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);
    }
    // *****************************
    // Event handling
    // *****************************
    /** Add the onClick event handler 
     * Implement the OnClickListener callback **/
    public void onClick(View v
    {
    // do something when the button is clicked      
      switch(v.getId())
      {
        case R.id.btnTest:
          // Create instance of the worker thread class
          moMyThreadClass = new MyThreadClass();
          // Start the worker thread
          moMyThreadClass.start();
          break;
      }
    }
}

The thread is started by calling the start() method of the thread class, this starts the new thread and calls the overridden run() method.

Passing Data to and From thread

An easy way to pass data to and from the thread is via public properties this way the UI thread can access the same variables as the worker thread.

[Code sample – public properties in thread class]
  // *********************************
    // Public properties
    // *********************************
    public double Latitude;
    public String Name;
  public String Email;
  public int Quantity;

In the thread class add public properties which can be accessed from the UI activity thread as follows.

[Code sample – accessing the properties from the UI thread]
          // Set values for use in the thread
          // These could come from anywhere
          // eg user input fields
          moMyThreadClass.Email = "jim@eigo.co.uk";
          moMyThreadClass.Name = "James T. Kirk";
          moMyThreadClass.Latitude = -3.2155899;
          moMyThreadClass.Quantity = 9;

This code would probably go in the on click event just before calling the .start() method.

Managing the Process

When the thread is started the run() method will be called and its code executed. To track if the thread is still running or stopped we can add a method and an internal property to the thread class.

[Code sample – stopping + starting the thread]
public class MyThreadClass extends Thread 
{
  // ***************************
    // Public Constants
    // ***************************
  public final static byte STATE_NOT_STARTED = 0;
  public final static byte STATE_RUNNING = 1;
  public final static byte STATE_DONE = 2;
  // *********************************
    // Private properties
    // *********************************
  // Used to stop a running threaded process
  private boolean mblnStop;
  // Keep track of the running status of the thread
  private byte mbytStatus = STATE_NOT_STARTED;
  // *********************************
    // Public properties
    // *********************************
    public double Latitude;
    public String Name;
  public String Email;
  public int Quantity;
  // **********************************
    // Public methods
    // **********************************
    // This method is run when the thread
  // is started by calling start().
  @Override
    public void run() 
    {
    // Flag that process does not need stopping
    mblnStop = false;
    mbytStatus = STATE_RUNNING;
    
      // Do something time consuming
    for (int i = 0; i < 10; i++)
    {
      // Check if we need to stop executing this code
      if (mblnStop)
        break// exit the loop to end the process
      
      try 
      {
        Thread.sleep(1000);
      
      catch (InterruptedException e
      {
        e.printStackTrace();
      }
    }
      
      mbytStatus = STATE_DONE;
    }
  
  // Set internal flag to stop threaded process
  public void StopThread()
  {
    mblnStop = true;
  }
  
  // Get the running status of the threaded process
  public byte GetStatus()
  {
    return mbytStatus;
  }
}

In the code sample above the public constants help us to easily enumerate the running state of the thread. The StopThread() method gives us a controlled way to bring the thread to a halt via the internal Boolean property mblnStop. The code being executed from the run() method can check the value of mblnStop between chunks of work and end its execution in a controlled way.

[Code sample – how to check status of thread and stop it from UI activity]
// Check if thread is running
if (moMyThreadClass.GetStatus() == MyThreadClass.STATE_RUNNING)
  moMyThreadClass.StopThread()// Stop the thread

Raising Events to UI Thread

Events can be raised from the thread and handled in the UI thread to cause updates to the UI perhaps to update a graph, a news feed or as we will see later a progress bar.

To raise events in the UI the worker thread will need a reference to a handler from the UI Activity, this can be used to generate messages. The UI Activity class will need to create the handler and pass its reference to the thread class when instantiating it. Data can be added to the messages sent to the UI, for example the results of a calculation so far.

[Code sample – thread class with event raising code]
public class MyThreadClass extends Thread 
{
  // ***************************
    // Public Constants
    // ***************************
  public final static byte STATE_NOT_STARTED = 0;
  public final static byte STATE_RUNNING = 1;
  public final static byte STATE_DONE = 2;
  
  public final static int MESSAGE_COMPLETE = 0;
  public final static int MESSAGE_ERROR = 1;
  public final static int MESSAGE_PROGRESS = 2;
  // *********************************
    // Private properties
    // *********************************
  // Used to stop a running threaded process
  private boolean mblnStop;
  // Keep track of the running status of the thread
  private byte mbytStatus = STATE_NOT_STARTED;
  // *********************************
    // Public properties
    // *********************************
    public double Latitude;
    public String Name;
  public String Email;
  public int Quantity;
  // Event handle from calling Activity
  public Handler HandlerOfCaller;
  // ***************************
    // Constructors
    // ***************************
    // Thread constructor
  MyThreadClass(Handler oHandler
    {
      HandlerOfCaller = oHandler;
    }
  // **********************************
    // Public methods
    // **********************************
    // This method is run when the thread
  // is started by calling start().
  @Override
    public void run() 
    {
      try
      {
      // Flag that process does not need stopping
      mblnStop = false;
      mbytStatus = STATE_RUNNING;
      
        // Do something time consuming
      for (int i = 0; i < 10; i++)
      {
        // Check if we need to stop executing this code
        if (mblnStop)
          break// exit the loop to end the process
        
          // Update the progress of the file upload
          Message oMessage = HandlerOfCaller.obtainMessage();
          int intPercentageComplete = i*10;
          oMessage.arg1 = intPercentageComplete;
          oMessage.what = MESSAGE_PROGRESS;
          HandlerOfCaller.sendMessage(oMessage);
        
        try 
        {
          Thread.sleep(1000);
        
        catch (InterruptedException e
        {
          e.printStackTrace();
        }
      }
        
        mbytStatus = STATE_DONE;
        
        // Return the value calculated in our process
        // Hardcoded to 1654 for demonstration purposes
        Message oMessage = HandlerOfCaller.obtainMessage();
        oMessage.arg1 = 1654;
        oMessage.what = MESSAGE_COMPLETE;
        HandlerOfCaller.sendMessage(oMessage);
    }
      catch (Exception ex
      {   
        // Return the error via the handler
        Message oMessage = HandlerOfCaller.obtainMessage();
        Bundle oBundle = new Bundle();
        String strMessage = ex.getMessage();
        oBundle.putString("Message", strMessage);
        oMessage.setData(oBundle);
        oMessage.what = MESSAGE_ERROR;
        HandlerOfCaller.sendMessage(oMessage);
    }
    }
  
  // Set internal flag to stop threaded process
  public void StopThread()
  {
    mblnStop = true;
  }
  
  // Get the running status of the threaded process
  public byte GetStatus()
  {
    return mbytStatus;
  }
}

In the code above the messages what integer property is used to flag the meaning the of the message, similar to the way the thread running status was enumerated before here the message type is enumerated using public constants defined in the thread class. A value must be set for the what property of the message but this can be any integer you wish to use.

Other useful properties of the message are arg1 and arg2 which can hold integer values, if you need to pass more data or other datatypes, eg a string, use the setData() method to pass the message an instance of a Bundle. Obviously the more data you pass and the more complex the datatypes then the more costly it is in processor and memory use.

[Code sample – UI activity class with event handling code]
public class MyMainUiActivity extends Activity  
implements OnClickListener
{
  // ***************************
    // Private variables
    // ***************************
  // Worker thread class
  private MyThreadClass moMyThreadClass = null;
    // ***************************
    // Activity Lifetime Events
    // ***************************
    /** 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);
    }
    // *****************************
    // Event handling
    // *****************************
    /** Add the onClick event handler 
     * Implement the OnClickListener callback **/
    public void onClick(View v
    {
    // do something when the button is clicked      
      switch(v.getId())
      {
        case R.id.btnTest:
          // Create instance of the worker thread class
          moMyThreadClass = new MyThreadClass(moHandlerOfThis);
          
          // Set values for use in the thread
          // These could come from anywhere
          // eg user input fields
          moMyThreadClass.Email = "jim@eigo.co.uk";
          moMyThreadClass.Name = "James T. Kirk";
          moMyThreadClass.Latitude = -3.2155899;
          moMyThreadClass.Quantity = 9;
          
          /* Example code - how to stop thread
          // Check if thread is running
          if (moMyThreadClass.GetStatus() 
== MyThreadClass.STATE_RUNNING)

            moMyThreadClass.StopThread(); // Stop the thread
          */
          
          // Start the worker thread
          moMyThreadClass.start();
          break;
    }
    }
    
    // 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;
            break;
          case MyThreadClass.MESSAGE_ERROR:
            // Show the error message 
            txtOutput.setText("Process failed, error = " 
                + oMessage.getData().getString("Message"));
            // Get rid of the sending thread
            moMyThreadClass.StopThread();
            moMyThreadClass = null;
            break;
          case MyThreadClass.MESSAGE_PROGRESS:
            // Show the progress message
            txtOutput.setText("Thread progress = " 
                + String.valueOf(oMessage.arg1"%");
            break;
            
          default:
            // This should never occur
            txtOutput.setText("Unknown message type = " 
                + String.valueOf(oMessage.what));
            // Get rid of the sending thread
            moMyThreadClass.StopThread();
            moMyThreadClass = null;
            break;
          }
        }
    };
    
}

In this code sample the Handler is passed to the worker thread on instantiation. The messages returned from the worker thread cause the handleMessage() function to be called. In this function the what property of the message is checked to decide how/if to respond to the event.

End of Part 1

The second part of this article describes how to show a progress dialog while running a thread and how to keep both worker thread and progress dialog alive during screen rotations (which destroy and recreate Activities).

 

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: