Skip to content
September 12, 2010 / Daniel Freeman

Dr Android answers: Threads and the UI.

I received the following question by email:-

I would be interested on how a different thread process can dispatch UI update/processing back to the main thread. To be a little more specific, I spawned a new Thread from the current main Activity to do some tasks but would like to see the UI get updated – displaying the result of the processing in the spawned thread all this while with Thread keeps running in the background.
Thanks. See ya.

Rgds,

Wilson

We might use Threads to prevent a time-consuming operation from interfering with the responsiveness of the UI Thread, and the user’s experience.  But there is one golden rule.  You can only affect the UI from the UI thread.  You can’t change the screen from a background thread.

For example, suppose we wanted to change something on the screen at regular time intervals.  Most Java developers would probably consider a Timer/TimerTask.  Like this:-

public class HelloTimer extends Activity {

  protected TextView textView;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    textView = new TextView(this);
    setContentView(textView);

    Timer ticktock = new Timer();
    ticktock.scheduleAtFixedRate(new UpdateTask(),0,500);
  }

  class UpdateTask extends TimerTask {
    public void run() {
      textView.setText(Double.toString(Math.random()));
    }
 }
}

The problem with the above is that it DOESN’T WORK.  Why?  Because it breaks the rule.  It updates the UI directly from a background thread.  We didn’t explicitly create a Thread, but how do you think a Timer works?  It sets up a background thread, which means the TimerTask is called from within a background thread.  And we’re not allowed to change the UI there.

So how do we update the UI at regular time intervals?  We use a Handler.  A Handler manages a queue of messages.  And we can place a message on the queue with a specific time delay.

public class HelloHandler extends Activity {

  protected TextView textView;
  protected Handler handler = new Handler();

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    textView = new TextView(this);
    setContentView(textView);

    handler.postDelayed(new UpdateTask(),500);
  }

  protected class UpdateTask implements Runnable {
    public void run() {
      textView.setText(Double.toString(Math.random()));
      handler.postDelayed(this, 500);
    }
  }
}

Notice how we call handler.postDelayed(this, 500); again after updating the UI, to set up the next delayed update. 500 is the delay in milliseconds (500ms = half a second).

It is this message handling class, the Handler, that we use to communicate between background threads and the UI Thread.  Here is an example:-

public class HelloThread extends Activity {

  protected TextView textView;
  protected Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      textView.setText((String)msg.obj);
    }
  };

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    textView = new TextView(this);
    setContentView(textView);
    UpdateTask updateTask = new UpdateTask();
    updateTask.start();
  }

  protected class UpdateTask extends Thread implements Runnable {
    public void run() {
      for (int i=0;i<60;i++) {
        try {
          Thread.sleep(1000); //This could be something computationally intensive.
          Message message = handler.obtainMessage();
          message.obj = Double.toString(Math.random());
          handler.sendMessage(message);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
}

Notice how new messages are instantiated using Message message = handler.obtainMessage(); and the actual message is being passed in message.obj property. This in an object into which we pass any information we wish.  (A random number in this example.)

In the final example, we’ve utilised an atomic boolean flag to suspend the background thread while the main UI Thread is stopped.  Atomic variables can only be accessed by one thread at a time.  (You can’t split the atom).  Hence we never read or write to it while is state is unreliable, mid-way through an update from another thread.

public class HelloThreadAtomic extends Activity {

  protected AtomicBoolean isRunning=new AtomicBoolean(false);
  protected UpdateTask updateTask = new UpdateTask();

  protected TextView textView;
  protected Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      textView.setText((String)msg.obj);
    }
  };

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    textView = new TextView(this);
    setContentView(textView);
  }

  public void onStart() {
    super.onStart();
    isRunning.set(true);
    updateTask.start();
  }

  public void onStop() {
    super.onStop();
    isRunning.set(false);
  }

  protected class UpdateTask extends Thread implements Runnable {
    public void run() {
      for (int i=0;i<60 && isRunning.get();i++) {
      try {
        Thread.sleep(1000); //This could be something computationally intensive.
        Message message = handler.obtainMessage();
        message.obj = Double.toString(Math.random());
        handler.sendMessage(message);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
     }
    }
  }
}

That’s it. Pass messages from a background Thread to the UI Thread using a Handler.

Any more questions, leave a comment to ask the doc.

Advertisements

One Comment

Leave a Comment

Trackbacks

  1. Mobile training support

To discuss MadComponents/MC3D, join the Facebook group!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: