【发布时间】:2016-07-13 00:23:12
【问题描述】:
我在 JavaFX(Java 版本 1.8.0_91)中遇到了一些奇怪的事情。我的理解是,如果想要从单独的线程更新 UI,则必须使用 Platform.runLater(taskThatUpdates) 或 javafx.concurrent 包中的工具之一。
但是,如果我有一个 TableView 并在其上调用 .setItems(someObservableList),我可以从单独的线程更新 someObservableList 并查看对我的 TableView 的相应更改,而不会出现预期的 Exception in thread "X" java.lang.IllegalStateException: Not on FX application thread; currentThread = X 错误。
如果我将TableView 替换为ListView,则会出现预期的错误。
情况 #1 的示例代码:从不同的线程更新 TableView,没有调用 Platform.runLater()--并且没有错误。
public class Test extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) throws Exception {
// Create a table of integers with one column to display
TableView<Integer> data = new TableView<>();
TableColumn<Integer, Integer> num = new TableColumn<>("Number");
num.setCellValueFactory(v -> new ReadOnlyObjectWrapper(v.getValue()));
data.getColumns().add(num);
// Create a window & add the table
VBox root = new VBox();
Scene scene = new Scene(root);
root.getChildren().addAll(data);
stage.setScene(scene);
stage.show();
// Create a list of numbers & bind the table to it
ObservableList<Integer> someNumbers = FXCollections.observableArrayList();
data.setItems(someNumbers);
// Add a new number every second from a different thread
new Thread( () -> {
for (;;) {
try {
Thread.sleep(1000);
someNumbers.add((int) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
情况 #2 的示例代码:从不同的线程更新 ListView,而不调用 Platform.runLater()--产生错误。
public class Test extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage stage) throws Exception {
// Create a list of integers (instead of a table)
ListView<Integer> data = new ListView<>();
// Create a window & add the table
VBox root = new VBox();
Scene scene = new Scene(root);
root.getChildren().addAll(data);
stage.setScene(scene);
stage.show();
// Create a list of numbers & bind the table to it
ObservableList<Integer> someNumbers = FXCollections.observableArrayList();
data.setItems(someNumbers);
// Add a new number every second from a different thread
new Thread( () -> {
for (;;) {
try {
Thread.sleep(1000);
someNumbers.add((int) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
请注意,唯一的区别是将data 实例化为ListView<Integer> 而不是TableView<Integer>。
那么这里给出了什么?这是因为在第一个示例中调用了TableColumn::setCellValueFactory() 吗?--这是我的直觉。我想知道为什么一个不会导致错误而另一个会导致错误,更具体地说,.setItems 调用如何将数据绑定到视图的规则是什么。
【问题讨论】:
-
itemsProperty 的处理方式有所不同。在 TableView 中,该属性将失效,旧引用存储在弱引用对象中。在 ListView 中,只有一个没有自定义无效方法的 SimpleObjectProperty。所以在 ListView 中,更改将在应用程序线程中排队,而在 TableView 中不是?!
-
奇案! 2 个观察结果: 1. ListView 示例在 imo 中的行为也不正确。第二次编辑后抛出异常。 2. 也许你是凭直觉去做的。将工厂更改为
num.setCellValueFactory(v -> new SimpleObjectProperty<>( v.getValue()));会产生(立即)异常。 -
JavaFX 工具包的 API 要求您更改属于 FX 应用程序线程上场景一部分的节点。如果您从不同的线程进行这些更改,API 根本没有定义这些行为:换句话说,在这种情况下,没有任何“正确行为”的概念。如果您违反此规则,该工具包会“尽最大努力”抛出运行时异常,但不保证在所有情况下都这样做 - 例如,它不会为了检查正确的线程而危及性能。
-
未定义的行为是未定义的行为。即使有一天它做了你想做的事,你仍然应该认为它是不正确的,因为它可能不会像你明天所期望的那样,或者在你更新到更新版本的 Java 之后,或者在另一台计算机上,或者当你从侧面看它时.问“为什么未定义的行为 X 的表现就像在情况 Y 中定义的那样?”通常没有多大意义。 undefined 的整个概念并不意味着“它会抛出错误”或“它会崩溃”。它的意思是“不要指望它”,这就是你得到的全部保证。这正是您正在到达这里的原因。
标签: java javafx concurrency javafx-8