【问题标题】:How to communicate between a thread and the GUI如何在线程和 GUI 之间进行通信
【发布时间】:2019-12-17 17:19:19
【问题描述】:

目前我正在用 Kotlin 和 Java 开发一个桌面应用程序,GUI 是用 JavaFX 制作的。 我有几个基本的 UDP 服务器(Kotlin 线程)监听不同的端口以通过网络接收数据。连接工作正常。

我可以想到几种(或多或少复杂)在线程和 GUI 控制器之间进行通信的方式,但我想知道是否有某种默认方法,我根本不知道.

我正在寻找一种从线程向控制器发送“消息”以更改标签上的某些文本或类似内容的简单方法。

【问题讨论】:

  • docs.oracle.com/javafx/2/threads/jfxpub-threads.htm。您可以做的一件事是绑定属性。您可以做的另一件事是在 Task succeeded 方法中更新 GUI。最后一个选项是使用Platform.runLater() 更新线程中的GUI 节点。我不推荐最后一个,因为如果你错过了什么,你的代码将是不可预测的。
  • FWIW,大多数 GUI 框架都有某种 perform-this-task-later-in-the-GUI-thread(...) 函数,类似于Platform.runLater() Sedrick 提到的。向另一个方向发送消息,从 GUI 事件处理程序到特定的后台线程,可能更像是自己动手做的事情。
  • 最终这是通过Platform#runLater(Runnable) 完成的,正如 Sedrick 和 Solomon 所提到的。可能有更高级别的 API 可以添加抽象层,例如 javafx.concurrent.Task。该类具有可以从执行Task 的后台线程调用的方法(例如#updateMessage(String)),以更新FX 线程上的属性(它还合并更新以避免淹没FX 线程)——但即使是Task最终使用runLater。由于您使用的是 Kotlin,您可能需要查看 TornadoFX,因为这可能会添加一些方便的 API。
  • 哇,感谢您的快速回复。我有一个完全独立的类,它运行线程,它应该更新 GUI。目前我不知道应该如何使用 runLater() 因为我无法从线程类访问相关的 GUI 元素。谁能澄清一下,也许有一个小代码示例。但是绑定可能对我有用...
  • 好的,知道了,谢谢。仍然打开:我有一个完全独立的类,它运行线程,它应该更新 GUI。目前我不知道应该如何使用 runLater() 因为我无法从线程类访问相关的 GUI 元素。谁能澄清一下,也许用一个小代码示例。

标签: java multithreading kotlin javafx


【解决方案1】:

使用Platform.runLater

当不在 JavaFX 线程上时,使用 Platform.runLater 调用最终将更新 GUI 的方法。

这样做将安排包装在runLater 中的代码稍后在JavaFX 应用程序线程上执行。

由于 JavaFX 系统被设计为单线程系统,这对于让系统始终如一地工作是必要的。

JavaFX 中更高级别的并发支持

JavaFX 也有higher level concurrency support 使用诸如Task 之类的东西,它们通过诸如updateMessage 之类的方法提供了一些通信支持。在 Task 的实现级别,updateMessage 只是在内部使用Platform.runLater,但在其实现中包含了一些额外的优化,以使其简单高效地使用。

通常,Tasks 和 Platform.runLater 有不同的(有时是互补的)关注点,请参阅Javafx: Difference between javafx.concurent and Platform.runLater?

特别是,对于侦听多个端口并将信息反馈给 GUI 的 UDP 服务器的要求,可能直接调用 runLater 是比使用 JavaFX 任务更合适的解决方案。

其他问题的答案

但是在我的情况下绑定可能会起作用

如果未将调用包装在Platform.runLater 调用中,请勿使用绑定、修改属性或从另一个线程触发更改侦听器。

绑定和更改侦听器并非设计用于多线程环境(例如,如果您同时修改绑定或更改不同线程上的侦听器,结果可能无法预测)。此外,如果您的绑定或更改侦听器最终触发了对 JavaFX 应用程序线程之外的活动场景图的更改,那么结果可能再次无法预测,因为场景图并非旨在以这种方式使用。

目前我不知道应该如何使用 runLater(),因为我无法从线程类访问相关的 GUI 元素。

几个不同的选项:

  1. 为线程类提供对 GUI 元素的访问,以便它可以在 runLater 调用中修改它们(这是为您设计的修改)或
  2. 让线程类调用另一个类的方法(通过runLater),该类确实知道 GUI 元素并可以进行适当的更改或
  3. 使用具有可观察属性的共享模型类,您已将其附加到已建立绑定或更改侦听器的控制器类,以根据对属性的更改更新 UI。在您的线程中,您应该 read(有关从 GUI 线程与另一个线程进行通信的方法,请参阅链接)并使用 runLater 将可观察属性写入共享模型类。

如您所见,在所有情况下,最终都会使用runLater 来安排涉及稍后在 JavaFX 线程上运行的 GUI 的工作,以在系统内强制执行线程安全。在所有情况下,如果您无法直接访问 GUI 元素,则需要与可以代表您进行任何必要修改的东西进行通信。

【讨论】:

  • 再次感谢。澄清一下:如果我在另一个线程中做一些事情而不是 FX 应用程序线程,我需要 Platform.runLater(),好吧。但我不太确定当前场景的控制器中发生的事情。我还需要控制器中的 runLater() 吗?
猜你喜欢
  • 2011-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-22
  • 2012-06-10
  • 1970-01-01
  • 1970-01-01
  • 2011-03-21
相关资源
最近更新 更多