I have a worker thread that sits in the background, processing messages. Something like this:
class Worker extends Thread {
public volatile Handler handler; // actually private, of course
public void run() {
Looper.prepare();
mHandler = new Handler() { // the Handler hooks up to the current Thread
public boolean handleMessage(Message msg) {
// ...
}
};
Looper.loop();
}
}
From the main thread (UI thread, not that it matters) I would like to do something like this:
Worker worker = new Worker();
worker.start();
worker.handler.sendMessage(...);
The trouble is that this sets me up for a beautiful race condition: at the time worker.handler
is read, there is no way to be sure that the worker thread has already assigned to this field!
I cannot simply create the Handler
from the Worker
's constructor, because the constructor runs on the main thread, so the Handler
will associate itself with the wrong thread.
This hardly seems like an uncommon scenario. I can come up with several workarounds, all of them ugly:
Something like this:
class Worker extends Thread { public volatile Handler handler; // actually private, of course public void run() { Looper.prepare(); mHandler = new Handler() { // the Handler hooks up to the current Thread public boolean handleMessage(Message msg) { // ... } }; notifyAll(); // <- ADDED Looper.loop(); } }
And from the main thread:
Worker worker = new Worker(); worker.start(); worker.wait(); // <- ADDED worker.handler.sendMessage(...);
But this is not reliable either: if the
notifyAll()
happens before thewait()
, then we'll never be woken up!Passing an initial
Message
to theWorker
's constructor, having therun()
method post it. An ad-hoc solution, won't work for multiple messages, or if we don't want to send it right away but soon after.Busy-waiting until the
handler
field is no longernull
. Yep, a last resort...
I would like to create a Handler
and MessageQueue
on behalf of the Worker
thread, but this does not seem to be possible. What is the most elegant way out of this?
HandlerThread
? – ShaunnagetLooper()
method blocks until we have aLooper
, then we can usenew Handler(worker.getLooper())
from the main thread to initialize theHandler
. That would solve the problem, right? – AntiguaHandlerThread
fit into yourWorker
pattern. Leastways, you'll explain it better than I could, since it was your problem and your implementation of a solution -- I just pointed out a helper class to address the problem. – Shaunna