What happens if a Handler posts a message to a thread after Looper.prepare() but before Looper.loop() has been called?
Asked Answered
J

1

12

Consider the following snippet:

Looper.prepare();
handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            getLooper().quitSafely();
        }
    };
for(int i = 0; i < urls.size(); i++) {
    useCaseProvider.get().execute(callback, handler, urls.get(i), threadPool);
}
Looper.loop();

//Continue processing the results of all the use cases after the    
//loop has been asked to terminated via the handler

A little background: I'm doing some processing on the UI thread where I will need to ping a large about of devices and do something with the result. I need to perform the requests in parallel to be efficient.

Question: If one of these use cases somehow executed fast enough and made a callback before I was able to hit Looper.loop(); would the message be queued or just lost? Callbacks are being posted back to this thread by the handler posting a runnable to the original thread.

Juristic answered 6/3, 2015 at 22:26 Comment(0)
B
7

Assuming you have invoked Looper.prepare() prior to your useCaseProvider delivering results, you should be fine. If Looper.prepare was not called you should be seeing RuntimeException being thrown.

The Looper object is tied to a thread local which hosts the message queue. The Looper.prepare function will construct this message queue at which point you can begin queuing up messages. Once you fire Looper.loop() that's when those pending messages will begin to execute.

Looking at the snippet, I'm not too sure how things are tied together. Generally you want to construct a looper like this:

private static final class MyThread extends Thread {
    private Handler mHandler;

    @Override
    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                // handle message
            }
        };

        Looper.loop();
    }

    public Handler getHandler() {
        return mHandler;
    }
}

I'm assuming your thread pool is then a pool of MyThread threads, each of which have their own Looper. The thread pool should initialize your threads so once you deliver a Runnable to be executed by your thread, the run() method should have the Looper initialized.

On the other hand, if you wish to associate your Handler with a particular looper (ie. you are not constructing the Handler within a thread like above) then you should be passing the Looper thread in to the constructor like:

Handler h = new Handler(myLooperThread);

If you don't specify that, then the handler uses the thread in which it was created to grab that thread's Looper from the ThreadLocal object.

Lastly if your intentions are to have messages delivered on the Handler which is associated with the UI thread then you should not be concerned about calling Looper.prepare or Looper.loop. This is handled by the Activity.

Bootee answered 9/3, 2015 at 16:55 Comment(2)
So, basically in his snippet processing messages on the MessageQueue will be delayed by the execution time of the for loop.Merow
Pretty much. Any attempt to deliver to an uninitialized Looper will result in a runtime exception. Depending on what's required, an IntentService might be a better way to go.Bootee

© 2022 - 2024 — McMap. All rights reserved.