更新 GUI:Runnables 与消息

Posted

技术标签:

【中文标题】更新 GUI:Runnables 与消息【英文标题】:Updating GUI: Runnables vs Messages 【发布时间】:2012-06-27 19:31:39 【问题描述】:

要从其他线程更新 GUI,主要有两种方法:

    将 java.lang.Runnable 与以下任何方法一起使用:

    Activity.runOnUiThread(Runnable)
    View.post(Runnable)
    View.postDelayed(Runnable, long)
    Handler.post(Runnable)
    

    使用android.os.Message:

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

您也可以使用 AsyncTask,但我的问题更侧重于更新一个非常简单的组件的用例。让我们看看如何使用这两种方法来完成:

    使用 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), ...
    

    使用消息:

    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?
        
    

恕我直言,消息不是要走的路,因为:

对于新的非 Android 程序员来说不容易理解(处理程序在构造期间挂钩到其线程)。 如果消息跨越进程边界,则对象负载应该是 Parcellable。 消息被重复使用(如果没有正确清理容易出错?) 处理程序具有双重作用(它发送消息,但也处理它们) 消息属性是公开的,但也提供 getter/setter。

另一方面,Runnables 遵循众所周知的命令模式,并且对程序员更加友好和可读。

那么使用 Messages 而不是 Runnables 有什么优势呢?在现代 Android 编程中,消息是否被推到了后台?有什么可以用 Messages 做而 Runnables 不能做的事情吗?

提前致谢。

【问题讨论】:

好问题! 【参考方案1】:

Handler 接口提供了比runOnUiThread() 更多的功能,根据文档:

处理程序有两个主要用途: (1) 安排消息和可运行文件在未来某个时间点执行 (2) 将要在与您自己的线程不同的线程上执行的操作排入队列。

runOnUiThread 仅执行 (2) 的子集。即“将要在 UI 线程 上执行的操作排入队列”

因此,除非您需要这些额外功能,否则 IMO runOnUiThread 是足够且首选的方式。

【讨论】:

好的,也许我的问题中没有明确地揭示这个想法。它不是 Handler vs Runnables,而是 Handler.sendMessage vs Runnable 替代方案。 (该组还包括Handler.postDelayedHandler.postAtTime,涵盖#1) 您实际上在帖子中很好地解释了差异。我不知道我可以添加任何其他差异。【参考方案2】:

Messages 可以重复使用,因此创建的对象更少,GC 更少。您最终也会得到更少的类和匿名类型。

一个很大的优势是发送MessageHandler 的类不需要知道关于Message 的实现的任何信息。这有助于封装,具体取决于它的使用位置。

最后考虑清洁度的区别

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

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

如果您有多个地方需要doStuff(),则前者的可读性更高,代码重复更少。

【讨论】:

Runnables 也可以重复使用,它也是一个接口,您可以以比您展示的更简洁的方式实例化一个(这也是我发布的 :))。 FWIW 我认为这里的关键是消息不需要类构建/发送它们来了解有关消息处理方式的任何信息。这是一种向客户公开您希望他们向您发送某些信息的方式,但不是将如何处理这些信息。如果您希望能够在运行时交换不同的处理程序而不会弄乱生成消息的代码,这也很方便。因此,对于发布的问题,像这样的简单 UI 更新与消息或可运行文件非常等效。但在某些情况下,消息确实提供了额外的好处。 消息之所以有用的主要原因是因为android操作系统实际上将它们排队,这就是为什么在任何时候你都可以确定稍后发送的消息只会在之前的消息之后执行发送已被执行。【参考方案3】:

我想说使用MessageRunnable 之间几乎没有区别。它主要归结为个人喜好。为什么?查看源代码,您会发现发布Runnable 使用完全相同的消息传递机制。它只是将Runnable 附加到Message 并发送。

4.4.2 源代码

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;

参考:Grep Code - Handler

【讨论】:

您好,您能否确认我们何时需要实现handleMessage 方法?我的意思是当我们需要 Handler 的子类时?因为我们可以通过调用 handler.post() 直接与主 UI 线程通信;方法。 建议将其作为新问题发布 在这里我创建了一个新问题。 ***.com/questions/47336380/…【参考方案4】:

比起Message,我更喜欢Runnable。我认为使用Runnable 的代码比Message 清晰得多,因为事件处理代码非常接近事件。此外,您还可以避免定义常量和 switch case 的开销。

而且我不认为使用Runnable 违反封装。您可以将Runnable.run() 中的代码提取到外部类中的另一个方法中,例如on...Event(),甚至可以将其包装到EventHandler 对象中。这两种方式都比使用Message 更清晰,尤其是当您需要在Message 中存储引用时,因为使用Runnable 可以避免向下转换msg.obj。而无名字段msg.obj 也容易出错,有时难以理解。

并且Runnable也可以通过将其存储为字段来重复使用。

【讨论】:

以上是关于更新 GUI:Runnables 与消息的主要内容,如果未能解决你的问题,请参考以下文章

与不是主线程的 GUI 线程通信

执行新的runnables时丢失ApplicationContext

将业务与表示逻辑分开的最佳方法?

Java:具有后台线程的 GUI 应用程序

我试图让我的 GUI 接收来自另一个应用程序的消息。但是,没有任何效果

Python Kivy:正确启动更新 GUI 元素的后台进程