【问题标题】:Javafx Updating UI from a Thread without direct calling Platform.runLaterJavafx 从线程更新 UI 而无需直接调用 Platform.runLater
【发布时间】:2015-02-03 06:06:22
【问题描述】:

现在有些人说不适合使用Platform.runLater() 从非 JavaFX 线程更新 UI,而 Oracle 网站引入了一种用于更新进度条的绑定方法。这里我要更新一个Label,所以这样编码:

Task task = new Task() {
    @Override
    protected Object call() throws Exception {
        int i = 0;
        while (true) {
            this.updateMessage("Count " + i);
            System.out.println(i);
            // Thread.sleep(10);
            i++;
        }
    }
};

Thread t = new Thread(task);
lbStatus.textProperty().bind(task.messageProperty());
t.start();

有效。

我想知道它足够好还是有其他方法可以考虑? 谢谢。

【问题讨论】:

标签: multithreading javafx task ui-thread concurrent-programming


【解决方案1】:

我不认为说使用Platform.runLater(...) 从后台线程更新 UI 是不“合适的”。在某些情况下,这样做是正确的,如Task Javadocs 所示。 javafx.concurrent API 提供的是一个“更高级别”的接口,可以满足您在编写多线程 JavaFX 应用程序时通常需要的功能。这个包中的类是由在多线程编程方面具有丰富专业知识的人编写的,因此它们可能已经解释了普通程序员可能没有意识到的细微之处。

例如,虽然updateMessage 最终调用Platform.runLater(...) 是正确的,但两者并不完全等价。如果你尝试同样的事情,直接致电Platform.runLater(..)

// Don't do this! It will make the UI unresponsive:
Task task = new Task() {
    @Override
    protected Object call() throws Exception {
        int i = 0;
        while (true) {
            Platform.runLater(() -> lblStatus.textProperty().set("Count "+i));
            i++;
        }
        return null ;
    }
};
Thread t = new Thread(task);

您的 UI 将变得(至少部分)无响应。原因是您在 FX 应用程序线程上安排了如此多的Runnables,它没有时间进行常规工作(渲染 UI、响应用户输入等)。 updateMessage(...) 的实现经过精心编写,以“限制”对 Platform.runLater(...) 的调用(它基本上将它们限制为每帧渲染一个)。该代码实现起来有点棘手:在您的代码示例中使用javafx.concurrent API 意味着您不必自己实现它。

因此,必须始终在 FX 应用程序线程上对 UI 进行更改,而安排这些更改的方法是通过 Platform.runLater(...)。实际上,您要么直接调用它,要么调用最终调用它的代码。但是,封装对 Platform.runLater(...) 的调用的一些 API 方法以非常复杂的方式执行此操作,当这些方法提供您需要的功能时,您可能更喜欢那些方法而不是自己进行调用。

【讨论】:

  • 感谢您的回复。是的,你是对的。直接调用 Platform.runLater() 卡住了 UI。 “包装对 Platform.runLater(...) 的调用的一些 API 方法”。你的意思是像 updateMessage() 这样的方法吗?说起这个颗粒状的代码,这个够用还是有其他方法可以考虑?
  • 是的,update*(...) 方法特别限制了对 UI 的调用;随意调用这些方法基本上是安全的。
【解决方案2】:

由于 Task 上的 updateMessage() 最终使用 Platform.runLater() 来更新 messageProperty,它不能替代代码中的直接 runLater()。所以本质是:

JavaFX 不是多线程的,任何 Porperty 绑定只有在 JavaFX 线程中修改了属性时才会起作用,例如通过使用 Platform.runLater()。

【讨论】:

  • 感谢您的回复。好的,但是当我们直接使用 platform.runlater() 时,它会导致 UI 完全挂起,当我们使用带有一些绑定的 updateMessage() 时,它工作正常。可能这就是为什么 oracle 也使用绑定来进行并发演示的原因。 docs.oracle.com/javafx/2/threads/jfxpub-threads.htm。问题是......我想知道这是否足够好,还是有其他方法可以考虑?
  • 如果在上例中调用Platform.runLater(),而不在循环中调用Thread.sleep(),系统什么时候挂起?这将挂起,因为:导致排队的“runLater()”任务超载!
  • 是的,无需调用 Thread.sleep()。使用绑定和 updateMessage() 时,即使循环内没有线程睡眠,它也不会挂起。
  • 如果您需要从其他线程以高速率更新 GUI 中显示的某些文本,我建议使用由您的线程修改的 StringProperty,它将由时间线以 60Hz 的频率轮询以解耦GUI 线程活动的高变化率....
  • 我不明白,你的意思是绑定一个 StringProperty 到 Label 并更新它?我假设目前正在执行此操作的代码。还是你做了别的事?如果是请给我一个例子。感谢您的快速回复。
猜你喜欢
  • 2018-05-24
  • 2013-02-16
  • 1970-01-01
  • 2015-10-26
  • 2014-05-11
  • 1970-01-01
  • 1970-01-01
  • 2014-12-17
  • 2017-01-31
相关资源
最近更新 更多