建议
仅当您需要在任务中为自定义属性提供属性样式界面时,才使用以下解决方案。通常,许多应用程序不需要这样的接口,只需调用一个 Platform.runLater 而不是公开自定义属性就足够了。
解决方案
您可以使用与message property 和Task 相同的习语。我只是将相关代码复制并粘贴到此答案中。请注意,此解决方案将通过AtomicReference 来“合并更新,以免我们淹没事件队列”。此解决方案不违背 JavaFX 的一般绑定性质,并且如果使用过于频繁,也不会导致主线程出现大量消息。但是,由于它合并更新,因此并非对属性的每次更新都会触发属性更改。每个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();
}
其他问题的答案
这是Task类的源代码?
是的。这是source code from Task。
所以你是说唯一的方法是用额外的属性来扩展 Task 类,就像上面的 Task 中所做的那样?
如果您希望自定义任务中的自定义属性可以同时修改,那么是的,您需要子类化任务。但这与将自定义属性添加到您定义的任何其他类(或扩展另一个现有类以添加属性)实际上并没有太大区别。唯一的区别是额外的机制来确保执行发生在正确的线程上并在需要时合并。
第二个话题,一开始你似乎也说偶尔调用runLater是一种可以接受的方式?
是的,Platform.runLater() 是在任务和 JavaFX UI 线程之间发送消息的推荐方式(如 Task javadoc 中所示)。
这些属性提供了任务和对象之间的松散耦合,这些对象可能通过observer pattern 依赖于任务。如果您不需要松散耦合,那么您不需要特别需要属性(尽管它们有时很有用且易于绑定,因为 JavaFX API 的其余部分,例如标签的文本,都是基于属性的) .