get main thread's message queue and handler

r.v picture r.v · Jul 2, 2011 · Viewed 13.8k times · Source

How do I get the message queue of the main thread from another thread? Looper.getMainLooper() gets the main thread's looper but I am unable to find a way to get the MessageQueue for another thread's looper. Moreover, how do I get the handler for the main looper? I am unable to find any way to get it.

Answer

Craig B picture Craig B · Feb 6, 2012

@r.v,

I had a similar need. I wanted to know when the MessageQueue is empty and when I post something for it to do and I want to know when it becomes empty with nothing remaining to do. I looked at the MessageQueue.IdleHandler and found that it didn't behave as I wanted to I came up with another solution.

In my case I wanted to use the Looper/Handler mechanism to sequentially execute file downloads. Basically each download I want to execute is wrapped in a Runnable. I only want one at a time to be running, so this pattern works well without having to dig into the nuts and bolts of a more involved threading solution. Additionally, I wanted to know when I first put something into the queue and it begins its work, and wanted to know when it was completely done (queue is empty).

I was able to use the handler's message mechanism to achieve this. The messages are handled in sequence with the Runnables, so you can strategically place messages in the queue to help you know the conditions of the queue. Unlike with Runnables in the Handler's queue, there are some query and removal abilities for messages that ultimately provide the solution.

What I do is each time I add a runnable to the Handler (via Handler.post), I also remove all instances of the custom QUEUE_EMPTY message, then add a fresh QUEUE_EMPTY message. This ensures that I have a QUEUE_EMPTY message at the end of the queue. Once I encounter the QUEUE_EMPTY message in my subclassed Handler, I know that I'm at the end of the queue. Additionally, if I don't find a QUEUE_EMPTY message in the queue when I go to add a runnable, I know that the queue was empty and the thread was idle.

As some will quickly point out, there are some real inefficiencies with this solution. Having to iterate through the queue for these "marker" messages could be a real performance issue if there were a large number of entries in the queue. In my case, I'm dealing with only a handful of file downloads at a time so any performance penalties are negligible. If you have a similar situation, I think this is a pretty reasonable solution. It would have been nice for the Android SDK to provide these basic abilities to the MessageQueue. I agree ideally you wouldn't want to mess with the MessageQueue, but knowing when it is idle/working/empty seem like reasonable things and I'm sure there are numbers of scenarios when there is value knowing these things.

    class DownloaderThread extends Thread
{
    private static final int QUEUE_EMPTY = 9999;
    private MyHandler handler;

    @Override
    public void run()
    {
        try
        {
            Looper.prepare();
            handler = new MyHandler();
            Looper.loop();
        }
        catch (Throwable t)
        {
            Log.e(TAG, "halted due to an error", t);
        }
    }

    public void post(Runnable r)
    {
        if(!handler.hasMessages(QUEUE_EMPTY))
        {
            Log.v(TAG, "Download queue was empty.  First element being added.");
        }

        handler.post(r);
        handler.removeMessages(QUEUE_EMPTY);
        handler.sendEmptyMessage(QUEUE_EMPTY);
    }

    class MyHandler extends Handler
    {
        @Override
        public void handleMessage(Message msg)
        {
            if(msg.what == QUEUE_EMPTY)
            {
                Log.v(TAG, "Download runnable queue is empty!");
            }
        }
    }
};