78

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:

  1. 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 the wait(), then we'll never be woken up!

  2. Passing an initial Message to the Worker's constructor, having the run() 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.

  3. Busy-waiting until the handler field is no longer null. 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?

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • 12
    Any particular reason you're not using `HandlerThread`? – CommonsWare Jan 29 '11 at 17:16
  • 2
    @CommonsWare: Hmm, wasn't aware that it existed. No cross-references in the docs. Its `getLooper()` method blocks until we have a `Looper`, then we can use `new Handler(worker.getLooper())` *from the main thread* to initialize the `Handler`. That would solve the problem, right? – Thomas Jan 29 '11 at 17:22
  • I think so. OTOH, I don't use it much myself, and so I may be missing something. – CommonsWare Jan 29 '11 at 17:29
  • 3
    @CommonsWare: It solved the problem. Now if you would post that as an answer, I will put a big green check mark next to it ;) – Thomas Jan 30 '11 at 21:55
  • 9
    Actually, I think it would be better if you answered it yourself, to explain how `HandlerThread` fit into your `Worker` 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. – CommonsWare Jan 30 '11 at 22:02

4 Answers4

64

Eventual solution (minus error checking), thanks to CommonsWare:

class Worker extends HandlerThread {

    // ...

    public synchronized void waitUntilReady() {
        d_handler = new Handler(getLooper(), d_messageHandler);
    }

}

And from the main thread:

Worker worker = new Worker();
worker.start();
worker.waitUntilReady(); // <- ADDED
worker.handler.sendMessage(...);

This works thanks to the semantics of HandlerThread.getLooper() which blocks until the looper has been initialized.


Incidentally, this is similar to my solution #1 above, since the HandlerThread is implemented roughly as follows (gotta love open source):

public void run() {
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Looper.loop();
}

public Looper getLooper() {
    synchronized (this) {
        while (mLooper == null) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
    }
    return mLooper;
}

The key difference is that it doesn't check whether the worker thread is running, but that it has actually created a looper; and the way to do so is to store the looper in a private field. Nice!

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • 1
    Thanks for this. I am commenting just to point out that the waitUntilReady() _must_ be called after worker.start() . Looking back it sounds pretty obvious, but it took me a bit to get what I was getting wrong while getting a null pointer exception. – fedepaol Apr 09 '12 at 21:43
  • Something similar as above being done in PowerManagerService in android fmk: `mInitComplete = false; mHandlerThread = new HandlerThread("PowerManagerService") { @Override protected void onLooperPrepared() { super.onLooperPrepared(); initInThread(); } }; mHandlerThread.start(); synchronized (mHandlerThread) { while (!mInitComplete) { try { mHandlerThread.wait(); } catch (InterruptedException e) { // Ignore } } }` (http://grepcode.com) – 1O1 Oct 25 '12 at 05:38
  • Why don't you initialise the handler in the constructor of the HandlerThread ? It would also ensure creation of the handler is unique. Actually, there is still a chance that waitUntilReady is called twice on the same Worker instance, and that could be hard to debug. – Snicolas Dec 29 '12 at 21:48
  • After nearly two years, I'm afraid I don't remember the intricacies of this problem. Adding a check to `waitUntilReady` is easy enough in any case. – Thomas Dec 30 '12 at 13:40
  • 4
    @Snicolas you can't, because the handler is bounded to the thread where it is created. Initializing it in HandlerThread's constructor would result in an handler in the main thread or wherever HandlerThread is created. If you want to use the Handler constructor with the Handler's looper as parameter (the one used in waituntilready) I'm pretty sure it would result in a deadlock. – fedepaol Dec 31 '12 at 09:06
  • 1
    Ok, that's a good point. Thx for posting. So to protect the handler from being misused you could wrap the handler methods into the HandlerThread that could act as a facade and delegate each call to the handler, *after* checking handler is initialized. That would be a better way to encapsulate it and prevent waitUntilReady from being forgotten. – Snicolas Dec 31 '12 at 09:36
  • 2
    @Thomas what is the main purpose of the `d_handler`? `d_messageHandler ` handles the messages, right? But you are sending meesage using `work.handler` – woyaru Jun 13 '14 at 14:18
  • 1
    This is very interesting, but it would be more useful if you provided some notes about what d_handler and d_messageHandler are, and how they're used. – RenniePet Dec 19 '15 at 22:51
  • @Thomas, I think this attaches the Handler to the main Thread because waitUntilReady is executed on the MainThread. Implementing methods in the class of the thread doesn't mean they are executed on it. They are only run on the Thread if you call them from the run() method. But you call it from the UI-Thread. – Sebi2020 May 18 '17 at 13:35
  • @Sebi2020 Forgive me if I don't remember the details 6 years later, but I guess passing the result of `getLooper()` takes care of that. – Thomas May 18 '17 at 16:00
1

take a look at the source code of HandlerThread

@Override
     public void run() {
         mTid = Process.myTid();
         Looper.prepare();
         synchronized (this) {
             mLooper = Looper.myLooper();
             notifyAll();
         }
         Process.setThreadPriority(mPriority);
         onLooperPrepared();
         Looper.loop();
         mTid = -1;
     }

Basically, if you are extending Thread in worker and implementing your own Looper, then your main thread class should extend worker and set your handler there.

Infinite Recursion
  • 6,511
  • 28
  • 39
  • 51
beebee
  • 280
  • 3
  • 18
1

This is my solutions: MainActivity:

//Other Code

 mCountDownLatch = new CountDownLatch(1);
        mainApp = this;
        WorkerThread workerThread = new WorkerThread(mCountDownLatch);
        workerThread.start();
        try {
            mCountDownLatch.await();
            Log.i("MsgToWorkerThread", "Worker Thread is up and running. We can send message to it now...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Toast.makeText(this, "Trial run...", Toast.LENGTH_LONG).show();
        Message msg = workerThread.workerThreadHandler.obtainMessage();
        workerThread.workerThreadHandler.sendMessage(msg);

The WorkerThread Class:

public class WorkerThread extends Thread{

    public Handler workerThreadHandler;
    CountDownLatch mLatch;

    public WorkerThread(CountDownLatch latch){

        mLatch = latch;
    }


    public void run() {
        Looper.prepare();
        workerThreadHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {

                Log.i("MsgToWorkerThread", "Message received from UI thread...");
                        MainActivity.getMainApp().runOnUiThread(new Runnable() {

                            @Override
                            public void run() {
                                Toast.makeText(MainActivity.getMainApp().getApplicationContext(), "Message received in worker thread from UI thread", Toast.LENGTH_LONG).show();
                                //Log.i("MsgToWorkerThread", "Message received from UI thread...");
                            }
                        });

            }

        };
        Log.i("MsgToWorkerThread", "Worker thread ready...");
        mLatch.countDown();
        Looper.loop();
    }
}
somenath mukhopadhyay
  • 1,548
  • 1
  • 16
  • 18
0
    class WorkerThread extends Thread {
            private Exchanger<Void> mStartExchanger = new Exchanger<Void>();
            private Handler mHandler;
            public Handler getHandler() {
                    return mHandler;
            }
            @Override
            public void run() {
                    Looper.prepare();
                    mHandler = new Handler();
                    try {
                            mStartExchanger.exchange(null);
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
                    Looper.loop();
            }

            @Override
            public synchronized void start() {
                    super.start();
                    try {
                            mStartExchanger.exchange(null);
                    } catch (InterruptedException e) {
                            e.printStackTrace();
                    }
            }
    }
18446744073709551615
  • 16,368
  • 4
  • 94
  • 127