Updating GUI: Runnables vs Messages
Asked Answered
H

4

51

To update the GUI from other threads, there are basically two main approaches:

  1. Use java.lang.Runnable with any of these methods:

    Activity.runOnUiThread(Runnable)
    View.post(Runnable)
    View.postDelayed(Runnable, long)
    Handler.post(Runnable)
    
  2. Use android.os.Message:

    Handler.sendMessage(Message) / Handler.handleMessage(Message)
    

You can also use AsyncTask, but my question is more focused on the use case of updating a very simple component. Let's see how it would be done using both approaches:

  1. Using Runnables:

    TextViev tv = ...;
    final String data = "hello";
    Runnable r = new Runnable() {
    
        @Override
        public void run(){
            tv.setText(data);
        }
    
    };
    //Now call Activity.runOnUiThread(r) or handler.post(r), ...
    
  2. Using messages:

    Message m = handler.obtainMessage(UPDATE_TEXT_VIEW, "hello");
    handler.sendMessage(m);
    
    //Now on handler implementation:
        @Override
        public void handleMessage(Message msg) {
            if(msg.what == UPDATE_TEXT_VIEW){
                String s = (String) msg.obj;
                tv.setText(data);
            } ... //other IFs?
        }
    

IMHO, Messages are not the way to go because:

  • Not easy to understand for new non-android programmers (handler hooks to its thread during construction).
  • Object payload should be Parcellable if the message crosses process boundaries.
  • Messages are reused (error prone if not properly cleaned up?)
  • The handler has a dual role (it sends messages, but also handles them)
  • Message attributes are public, but also offer getter/setter.

On the other hand, Runnables follow the well known command pattern, and are more programmer-friendly and readable.

So what are the advantages of using Messages over Runnables? Are Messages pushed into the background in modern day Android programming? Is there anything you can do with Messages that can't be done with Runnables?

Thanks in advance.

Harwell answered 26/6, 2012 at 13:12 Comment(1)
Excellent question!Isologous
A
22

I would say there is little difference between using a Message vs a Runnable. It'll mostly boil down to personal preference. Why? Looking at the source code you'll find that posting a Runnable uses the same exact messaging mechanism. It simply attaches the Runnable to a Message and sends that.

4.4.2 Source Code

public final boolean post(Runnable r) {
    return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

Ref: Grep Code - Handler

Algolagnia answered 20/3, 2014 at 13:8 Comment(3)
Hi can you confirm when we need to implement handleMessage method? I mean when we need child class of Handler? because we can direct communicate to Main UI thread by calling handler.post(); method.Encephalography
Suggest posting that as a new questionAlgolagnia
Here I have created a new question. #47336880Encephalography
S
16

Messages can be reused, so it results in fewer objects created and less GC. You also end up with fewer classes and anonymous types.

One big advantage is that a class sending a Message to a Handler doesn't need to know anything about the implementation of that Message. That can aid in encapsulation depending on where it's used.

Lastly consider the difference in cleanliness between

mHandler.obtainMessage(DO_STUFF, foo).sendToTarget();

vs

final Foo tempFoo = foo;
mHandler.post(new Runnable(){
    @Override
    public void run(){
        doStuff(tempFoo);
    }
};

If you have several places where you would have to doStuff(), the former is MUCH more readable and you'll have less code duplication.

Sarene answered 25/11, 2013 at 23:27 Comment(3)
Runnables can also be reused, it's also an interface and you could instantiate one in a cleaner way than the one you shown (which is also the one I posted :) ).Harwell
FWIW I think the key here is that Messages don't require the class building/sending them to know anything about the way the message will be handled. It's a way to expose to a client that you want them to send you certain information, but not what will be done with it. It's also handy if you want to be able to swap in different handlers at run time without messing with the code that generates the messages. So in the case of the question posted, a simple UI update like this is pretty equivalent with a message or a runnable. But there are certainly cases where messages do offer added benefit.Characharabanc
The main reason imo why Messages would be useful is because the android OS actually queues them up, thats why at any point in time you can be sure that the messages sent later will only execute after the previous messages sent have been executed.Vaccine
M
4

Handler interface provides much more functionality than runOnUiThread(), according to docs:

There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed as some point in the future
(2) to enqueue an action to be performed on a different thread than your own.

runOnUiThread does only a subset of (2). ie "enqueue an action to be performed on UI thread"

So IMO unless you need those extra features runOnUiThread is sufficient and preferred way.

Melancholic answered 26/6, 2012 at 13:27 Comment(2)
Ok, maybe the idea is not clearly exposed in my question. It is not Handler vs Runnables, but Handler.sendMessage vs the Runnable alternatives. (This group includes also Handler.postDelayed and Handler.postAtTime which cover #1)Harwell
You actually explained the difference well in your post. I don't know any other difference I can add to those.Melancholic
S
3

I prefer Runnable to Message. I think code using Runnable is much clearer than Message, because the event handling code is very close to the event. Also, You can avoid the overhead of defining constants and switch cases.

And I don't think using Runnable violates encapsulation. You can extract the code in Runnable.run() into another method in the outer class, for example on...Event(), or even wrap it into an EventHandler object. Both ways are much clearer than using Message, especially when you need store references in Message, because using Runnable avoids downcasting msg.obj. And the nameless field msg.obj is also error prone and sometimes inefficient to understand.

And Runnable can also be reused by storing it as a field.

Stodgy answered 23/8, 2016 at 8:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.