Handlers initialized with Looper.getMainLooper() does not respond to message callbacks
Asked Answered
D

2

6

I am trying to implement Handlers listening on the same Looper from different threads.

Below I have two Handlers, one created in the main thread, another in the child thread, however both are initialized to listen on the Main Looper.

private Handler mMain;
public static final ThreadPoolExecutor tpe =
        (ThreadPoolExecutor) Executors.newCachedThreadPool();

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mMain = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            Log.wtf("", "main:" + msg);
        }
    };

    tpe.execute(new Runnable() {
        private Handler tChild = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                Log.wtf("", "child:" + msg);
            }
        };

        @Override
        public void run() {
            Log.wtf("", "send msg to main looper");
            tChild.sendEmptyMessage(100);
        }
    });
}

But when I send a message like below, only the child handler prints the message. The main handler does not receive the message.

03-20 22:02:26.754: A/(12857): send msg to main looper
03-20 22:02:26.847: A/(12857): child:{ what=100 when=-8ms }

What am I doing wrong? Thank you for reading.

Doubtful answered 21/3, 2013 at 2:9 Comment(0)
S
12

Each Handler instance controls the Message target and there is no way to get them to share, so every message or post sent to a Handler is only executed by that instance.

The Looper indicates which thread the messages/runnables sent will be executed on. In your code, both Handlers will execute handleMessage() on the main thread, despite being created on separate threads. That is the real reason you can pass a Looper instance to a Handler...if you pass no Looper, then the Handler will execute code on the thread in which it was created (which must also be a Looper thread).

Furthermore, because of this there isn't reason to create multiple Handlers to post data in this manner. A single Handler is designed to be sent messages from multiple threads, and they are all serialized in a MessageQueue and executed on the chosen Looper thread. You can post directly to mMain from the background thread to execute code on that thread. In this case, passing the Looper is redundant at that code is already on the main thread.

Shelia answered 21/3, 2013 at 2:31 Comment(3)
I see, thanks! Suppose if one day the Runnable was an outer class instead of an inner class, I should pass an initialized Handler to it via its constructor, right?Doubtful
The architecture would be application dependent, but you would need a reference to the Handler from wherever you plan to post messages to it.Shelia
Why not just use one Handler(this.getMainLooper()) in the Application context (the class that extends Application)? Wouldn't this be cleaner than doing it in an Activity?Hortatory
G
1

Messages sent to a Handler will only be handled by that Handler, even if it's sharing a Looper.

Buried in the source code for Handler is the line

msg.target = this;

This ensures no other Handler will touch it.

Guyette answered 21/3, 2013 at 2:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.