Followers

Thursday 18 November 2010

Handler Puzzle

I was making a small update to MagicPlayer and stumbled upon this puzzling way of handling concurrency.

Can you figure out what it does? (Some irrelevant code snipped)

Class: TracData



public class TrackData extends Handler {
String artist, album, track;
CallBack cb;
Context ctx;
public TrackData(Context ctx, CallBack cb) {
this.ctx = ctx;
this.cb = cb;
}

@Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
cb.setData(this.artist, this.album, this.track);
}
}

public void get(final int sid) {
new Thread() {
public void run() {
TalkWithTheServer();
obtainMessage(1).sendToTarget();
}
}.start();
}

interface CallBack {
public void setData(String artist, String album, String track);
}
}


And another Class: TrackInfo



public class TrackInfo extends Activity implements TrackData.CallBack {
TextView txtArtist;
TextView txtAlbum;
TextView txtTrack;
@Override
public void onCreate(Bundle savedInstanceState) {
SetupStuff();
fetchData(id_of_song_that_is_playing);
}

private void fetchData(int sid) {
TrackData d = new TrackData(this, this);
d.get(sid);
}

@Override
public void setData(String artist, String album, String track) {
txtArtist.setText(artist);
txtAlbum.setText(album);
txtTrack.setText(track);
}
}



!!!!!SPOILER!!!!!


TrackData is background fetcher which gets some data from my server and upon receiving it, updates the text fields in the UI. It looks a bit weird, but I figured this was clean way of doing it with as little code as possible.

TrackInfo is just an Activity with some fields for the returned data. It implements the TracData's inner interface to receive the data when TrackData has done talking.

How to handle handlers?


Flattr this


One of the first problems I encountered with Android, was that it crashed when I tried to interact with UI components from a thread!
I haven't managed to dig into why exactly it was designed this way, but there is only one "UI Thread" per application which is allowed to modify UI components.

So, the question is, what to do when you want to do a long running action in background and let the user know what is going on?

Introducing: Handler

Handler is a component which allows you to create messages from other threads and receive them in the UI thread.
Let's get familiar with it through a common example: ProgressBar

Very straightforward way to use this, is to create an inner class or anonymous instance and override the handleMessage function. However, this is also a quick way to bloat up your activity class and make it unreadable.

So, my way of using it is as so:
1. Create a Handler which get a reference to the bar we want to update:


package net.fizzl.example;

import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;

public class FooHandler extends Handler {
private ProgressBar pbar;
public FooHandler(ProgressBar pbar) {
this.pbar = pbar;
}

@Override
public void handleMessage(Message msg) {
if(msg.what == MSG_PROGRESS) {
pbar.setProgress(msg.arg1);
}
}

// Message Names
public static final int MSG_PROGRESS = 1;
}

2. Create a Thread that gets a reference to the handler.


package net.fizzl.example;

import android.os.Handler;
import android.os.Message;

public class FooThread extends Thread {
private Handler h;
public FooThread(Handler h) {
this.h = h;
}

public void run() {
while(dostuff) {
int progress = dostuff();
Message msg = h.obtainMessage(FooHandler.MSG_PROGRESS, progress, 0);
msg.sendToTarget();
}
}
}

3. Use these in your Activity:


ProgressBar pb = findViewById(R.id.theprogressbar);
FooHandler h = new FooHandler(pb);
FooThread t = new FooThread(h);
t.start();
4. Profit!

That's about it. This ofcourse assumes your actual ProgressBar has been set up beforehand to work with the expectations of the thread that sends messages to it.

What happens here, is like so:
1. Thread starts running. It gets progress from its internal function that "does stuff".
2. Thread asks the Handler to give it a prepared message with 'what' set to the value that indicates that it should update the status bar.
3. Thread sends the message.
4. Handler adds the message to its queue.
5. When Handlers message pump is called in the UI Thread, it finds the message and executes its handleMessage.
6. Now that we are running in UI Thread, we can safely call the ProgressBars setProgress.
UI Thread redraws stuff etc. Thread keeps on running and posting messages.


Anyway, I hope you enjoyed this write up and can make a bit more responsive UI's from now on!


Magic Player

Just a brief plug before I'll write the actual article.

I have revamped and republished Magic Player.

It is an app which you can use to listen to Jamendo provided music.

I hold a complete index of Jamendo songs on my own server and the application uses my server to get random songs to play.

You may rate the songs. It will save the rating and the time in the song it was rated. Feel free to rate multiple times per song!
Currently the rating data is not used for anything, but I plan to use it in the future to try and select song which are similar to those you have rated highly before. I'll plan to use tags, genres and user cross-comparisons to do this.

So, anyway, give it a try: