i have a JavaFX Application and in there a concurrency Task. While the Task is running, i want to append the message from updateMessage() to a TextArea
because the binding doesn't append new text to the TextArea, i used a ChangeListener
worker.messageProperty().addListener((observable, oldValue, newValue) -> {
ta_Statusbereich.appendText("\n" + newValue);
});
That is working but not on every change. I checked it with a System.out.println() and counted in the task from 1 to 300
for (Integer i = 1; i <= 300; i++) {
updateMessage(i.toString());
System.out.println(i.toString());
}
this println() in the Task gives me what i want 1,2,3,4,5,6,7,8 and so on, but my TextArea shows 1,4,5,8,9 i then added a println in the ChangeListener and get the same result, 1,4,5,8,9 (the result is random not always 1,4,5...)
why ? are there other ways to append the message text to the TextAres, maybe with bind ?
The
message
property is designed as a property which holds a "current message" for thetask
: i.e. the target use case is something akin to a status message. In this use case, it doesn't matter if a message that is stored in the property for only a very brief time is never intercepted. Indeed, the documentation forupdateMessage()
states:(my emphasis). So, in short, some values passed to
updateMessage(...)
may never actually be set as the value ofmessageProperty
if they are superceded quickly by another value. In general, you can expect only one value to be observed every time a frame is rendered to the screen (60 times per second, or fewer). If you have a use case where it is important you want to observe every value, then you need to use another mechanism.A very naïve implementation would just use
Platform.runLater(...)
and directly update the text area. I do not recommend this implementation, as you risk flooding the FX Application Thread with too many calls (the exact reason whyupdateMessage(...)
coalesces calls), making the UI unresponsive. However, this implementation would look like:Another option is to make each operation a separate task, and execute them all in parallel in some executor. Append to the text area in each task's
onSucceeded
handler. In this implementation, the order of the results is not predetermined, so if order is important, this is not an appropriate mechanism:If you want to do all this from a single task, and control the order, then you can put all the messages into a
BlockingQueue
, taking messages from the blocking queue and placing them in the text area on the FX Application thread. To ensure you don't flood the FX Application thread with too many calls, you should consume the messages from the queue no more than once per frame rendering to the screen. You can use anAnimationTimer
for this purpose: it'shandle
method is guaranteed to be invoked once per frame rendering. This looks like: