2

currently I' developing a desktop application with Kotlin and Java, the GUI is made with JavaFX. I have several basic UDP servers (Kotlin threads) listening to different ports to receive data over a network. The connections are working fine.

I can think of several (more or less complicated) ways of communicating between the threads and the controller of the GUI, but I'm wondering if there is some kind of default approach to this, that I simply don't know.

What I'm looking for is a simple way to send "messages" from a thread to the controller in order to change some text on a label or anything like that.

iffuw
  • 89
  • 2
  • 10
  • 1
    https://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm. One thing you can do is bind properties. Another thing you can do is update the GUI in the `Task` `succeeded` method. The last option is to use `Platform.runLater()` to update GUI nodes in the Thread. I would not recommend the last because if you miss something, your code will be unpredicatable. – SedJ601 Dec 17 '19 at 17:30
  • 1
    FWIW, _most_ GUI frameworks have some kind of a perform-this-task-later-in-the-GUI-thread(...) function, similar to the `Platform.runLater()` that Sedrick mentioned. Sending messages in the other direction, from GUI event handlers to a specific background thread, can be more of a do-it-yourself affair. – Solomon Slow Dec 17 '19 at 17:35
  • 1
    Ultimately this is accomplished with `Platform#runLater(Runnable)`, as mentioned by both Sedrick and Solomon. There may be higher level APIs available that add layers of abstraction, such as `javafx.concurrent.Task`. That class has methods (e.g. `#updateMessage(String)`) which can be called from the background thread executing the `Task` in order to update properties on the FX thread (it also coalesces updates to avoid flooding the FX thread)—but even `Task` uses `runLater` eventually. Since you're using Kotlin you may want to look into _TornadoFX_ as that may add some convenient API. – Slaw Dec 17 '19 at 17:58
  • Wow, thanks for the quick repies. I have a completely separate class, that runs the thread, which should update the GUI. At the moment I don't get how I should use the runLater() since I have no access to the relevant GUI elements from the thread class. Could anyone clarify thar, maybe with a small code example. However binding might work in my case... – iffuw Dec 17 '19 at 18:33
  • Okay, got it, thanks. Still open: I have a completely separate class, that runs the thread, which should update the GUI. At the moment I don't get how I should use the runLater() since I have no access to the relevant GUI elements from the thread class. Could anyone clarify thar, maybe with a small code example. – iffuw Dec 17 '19 at 19:04

1 Answers1

3

Use Platform.runLater

When not on the JavaFX thread, use Platform.runLater to invoke a method which will ultimately update the GUI.

Doing this will schedule the code wrapped in runLater to execute later on the JavaFX application thread.

As the JavaFX system is designed as a single-threaded system, this is necessary to allow the system to work consistently.

Higher level concurrency support in JavaFX

JavaFX also has higher level concurrency support using things such as Task which have some communication support via methods such as updateMessage. At an implementation level within Task, updateMessage just uses Platform.runLater internally, but includes some additional optimizations in its implementation to make it simple and efficient to use.

Usually, Tasks and Platform.runLater have different (sometimes complementary) focuses, see Javafx: Difference between javafx.concurent and Platform.runLater?.

In particular, for your requirement of UDP servers listening on multiple ports and feeding info back to a GUI, likely invoking runLater directly is a more appropriate solution than using a JavaFX Task.

Answers to additional questions

However binding might work in my case

Do not use bindings, modify properties or trigger change listeners from another thread without wrapping the call in a Platform.runLater call.

Binding and change listeners are not designed to work in a multi-threaded environment (for example if you concurrently modify the bindings or change listeners on different threads, the outcome may be unpredictable). Plus, if your binding or change listeners ultimately trigger changes to the active scene graph off of the JavaFX application thread, the outcome, again, may be unpredictable, as the scene graph is not designed to be used in that way.

At the moment I don't get how I should use the runLater() since I have no access to the relevant GUI elements from the thread class.

A few different options:

  1. Provide access to the GUI elements to the thread class, so it can modify them in a runLater call (this is a design modification for you) OR
  2. Have the thread class invoke a method on another class (via runLater) which does know about the GUI elements and can make the appropriate changes OR
  3. Use a shared model class with observable properties, which you have attached to a controller class that has established bindings or change listeners to update the UI based upon changes to the properties. From your thread you should read (see the link for a way to communicate from the GUI thread to another thread) and write the observable properties to the shared model class using runLater.

As you see, in all cases, eventually runLater is used to schedule work which involves the GUI to be run later on the JavaFX thread, to enforce thread-safety within the system. And in all cases, if you don't have direct access to the GUI elements, you need to communicate with something that does so that it can make any necessary modifications on your behalf.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • Thanks again. Just to clarify: If I'm doing some stuff in another Thread than the FX application thread, I need Platform.runLater(), okay. But I'm not quite sure on the stuff happening in the controller of the current scene. Do I need runLater() within the controller as well? – iffuw Dec 19 '19 at 09:23