Advice
Only use the solution below if you require a property style interface on your task for a customized property. In general, many applications do not require such an interface and a single Platform.runLater call rather than exposing a custom property will suffice.
Solution
You can use the same idiom as is used for the message property of a Task. I'll just copy and paste relevant code into this answer. Note that this solution will "coalesce updates such that we don't flood the event queue", by means of an AtomicReference. This solution does not go against the general bind nature of JavaFX and does not cause a lot of messages for the main thread if used too often. However, because it coalesces updates, not every update to the property will fire a property change. Property changes are only fired at most once per pulse.
private final StringProperty message = new SimpleStringProperty(this, "message", "");
@Override public final String getMessage() { checkThread(); return message.get(); }
@Override public final ReadOnlyStringProperty messageProperty() { checkThread(); return message; }
/**
* Used to send message updates in a thread-safe manner from the subclass
* to the FX application thread. AtomicReference is used so as to coalesce
* updates such that we don't flood the event queue.
*/
private AtomicReference<String> messageUpdate = new AtomicReference<>();
/**
* Updates the <code>message</code> property. Calls to updateMessage
* are coalesced and run later on the FX application thread, so calls
* to updateMessage, even from the FX Application thread, may not
* necessarily result in immediate updates to this property, and
* intermediate message values may be coalesced to save on event
* notifications.
* <p>
* <em>This method is safe to be called from any thread.</em>
* </p>
*
* @param message the new message
*/
protected void updateMessage(String message) {
if (isFxApplicationThread()) {
this.message.set(message);
} else {
// As with the workDone, it might be that the background thread
// will update this message quite frequently, and we need
// to throttle the updates so as not to completely clobber
// the event dispatching system.
if (messageUpdate.getAndSet(message) == null) {
runLater(new Runnable() {
@Override public void run() {
final String message = messageUpdate.getAndSet(null);
Task.this.message.set(message);
}
});
}
}
}
// This method exists for the sake of testing, so I can subclass and override
// this method in the test and not actually use Platform.runLater.
void runLater(Runnable r) {
Platform.runLater(r);
}
// This method exists for the sake of testing, so I can subclass and override
// this method in the test and not actually use Platform.isFxApplicationThread.
boolean isFxApplicationThread() {
return Platform.isFxApplicationThread();
}
Answers to additional questions
this is the source code from class Task?
Yes. This is source code from Task.
So you're saying the only way is to extend the Task class with additional properties like is done in Task above?
Well if you want custom properties in a custom Task that can be concurrently modified, then yes you need to subclass task. But this really not much different than adding custom properties to any other class you define (or extending another existing class to add properties). The only difference is the extra machinery to ensure that executions occur on the correct thread and are coalesced if needed.
Second topic, you also seem to say at the start that occasionally calling runLater is an acceptable way to do it?
Yes, Platform.runLater() is the recommended way to message between tasks and the JavaFX UI thread (as demonstrated in the Task javadoc).
The properties provide for a loose-coupling between the task and objects which may rely on the task through an observer pattern. If you don't require the loose-coupling, then you don't specifically require properties (though they can sometimes be useful and simple to bind to, as the rest of the JavaFX API, such as a label's text, is property based).