Google Android Development Agency SASS

Tuesday 17 February 2009

Accessing UI elements from background threads in Android

One of the challenges I've faced this week in Android has been how to access and change the UI from a background worker thread.

By default in Android, anything created in the onCreate() method of an Activity runs in the UI thread. Whilst this is good for some apps as it keeps the application profile down, for anything requiring a degree of performance it's not ideal.

We're working on a real-time music application here at SASS Digital and hence a lot of our work is done in background threads. To make matters worse, we need to execute code at specified intervals whilst maintaining performance.

The problem is, you cannot modify UI elements from a background thread.

Android goes some way to providing the solution to this in the form of the Handler class. The Handler allows you to execute Runnable objects whilst maintaining access to the UI elements. It also provides the seemingly useful postDelayed() method which allows you to schedule some code to run after a specified interval, much like the setInterval javascript statement.

All was looking rosy until it turned out that the stability of the Handlers timing was questionable to say the least... I couldn't get it to tick away perfectly no matter what I tried, so I was back to using a java.util.Timer for my timings, which meant I had no access to the UI again.

The solution turned out to be pretty simple.

You need to create a new Handler object and overide the handleMessage method to receive messages from your background threads.


In my onStart() method of the Activity I added this:

Handler h = new Handler() {
public void handleMessage(Message msg) {
//--update your UI here
}
};


I then implemented my custom TimerTask which would perform my work in the background, passing the Handler to it's constructor. E.g


MyTimerTask mtt = new MyTimerTask(h);
Timer t = new Timer();
t.schedule(mtt, 1, 200);


Now within the TimerTask I can pass messages back to my main UI thread via the Handler:


public void run() {
//--send a message to the UI thread to update itself
Message msg = _h.obtainMessage(); //_h is my Handler reference
msg.obj = 1;
_h.sendMessage(msg);
}


As if by magic my main UI code receives this message and can be notified to update the UI. It runs a lot quicker than using the default Handler.postDelayed().

Update


If you use an inner instantiation of a Runnable rather than a seperate class you don't even need to use a messaging pattern, you can use the Handler object to directly call methods on your UI thread using the post method:



Handler h = new Handler();

private void updateGUI() {
//--do your gui work here
}

private void doMyBackgroundWork() {
//--this method will do your background work

//--use the handler to call back to your main ui thread
h.post(updateGUI);
}

private Runnable bgRunnable = new Runnable() {
public void run() {
doMyBackgroundWork();
}
};

Thread t = new Thread(null, doMyBackgroundWork,"BGThread");
t.start();



Very simple!

No comments:

Post a Comment