下面的问题是我的JavaFX应用程序中的一个bug的来源。奇怪的是,行为取决于特定的JavaFX属性是否附加了侦听器。当ChangeListener
观察到属性时,一切都正常,否则不正常。快把我逼疯了。。
我设法把它分解成一个最小的代码示例。首先,我们需要一个类来公开一个不时更改的属性。在这里它被称为nameProperty()
。在这个例子中,我选择生成一个单独的线程,它会不断地修改属性,但在真实的的应用程序中,这是通过用户交互实现的。
class TestClass {
private final SimpleObjectProperty<String> name = new SimpleObjectProperty<>();
public TestClass() {
new Thread(() -> {
while(true) {
try {
Thread.sleep(1000);
Platform.runLater(() -> name.set("A"));
Thread.sleep(1000);
Platform.runLater(() -> name.set("B"));
}
catch(InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}
public ReadOnlyObjectProperty<String> nameProperty() {
return name;
}
}
在main方法中,在FX应用程序线程上,使用提取器创建ObservableList<TestClass>
,以便ListChangeListener
报告对nameProperty()
列表元素的更改。然后,我们创建测试类的一个示例,将其添加到列表中,并添加一个ListChangeListener
来观察列表的更新。
public static void main(String[] args) throws Exception {
Platform.startup(() -> {
Callback<TestClass, Observable[]> extractor = obj -> new Observable[]{ obj.nameProperty() };
ObservableList<TestClass> list = FXCollections.observableArrayList(extractor);
TestClass test = new TestClass();
list.add(test);
list.addListener((ListChangeListener<TestClass>) c -> {
while(c.next()) {
if(c.wasUpdated()) {
System.out.println("List element was updated");
}
}
});
});
Thread.sleep(60*60*1000);
}
由于列表提取器和属性不断被修改,我对输出的期望是它看起来像这样:
List element was updated
List element was updated
List element was updated
List element was updated
List element was updated
...
但它看起来像这样:
List element was updated
*silence*
现在奇怪的是,只要在代码中的任何地方将ChangeListener
添加到nameProperty()
,例如
test.nameProperty().addListener(((observable, oldValue, newValue) -> {}));
它看起来像预期的那样工作,并且列表不断地产生更改通知。
仅仅观察一个属性不应该改变绑定到该属性的其他事物的行为,对吗?但如果这是JavaFX中的一个bug,在我看来,这将是一个非常明显和基本的bug。所以也许我确实搞砸了一些事情,尽管程序看起来很简单。顺便说一下,我在Windows 10上使用OpenJFX版本21。
1条答案
按热度按时间wbgh16ku1#
注意,提取器返回一个
Observable
数组,如果我们看documentation:此类的实现应努力生成尽可能少的事件,以避免在事件处理程序中浪费太多时间。当第一个失效事件发生时,这个库中的实现将自己标记为无效。它们不再生成失效事件,直到它们的值被重新计算并再次有效。
name
属性以有效状态开始。您设置了该属性,它就失效了,触发了一个失效事件,最终列表更改侦听器会收到更新的通知。但是您永远不会查询name
属性,这意味着它永远不会被验证。因此,它保持在无效状态,并且不触发任何更多的无效事件,尽管它被反复设置(为不同的值)。如果您希望继续收到失效事件的通知,则需要验证可观察对象。下面是一个例子,展示了验证Observable和不验证Observable之间的区别:
输出量:
您还必须小心垃圾收集。在您的代码中,在更新它以解决存在的并发问题之后,我相信在
Runnable
(传递给startup
)返回之后,ObservableList
本身可能适合进行垃圾收集。