我有一个 JavaFX 表,它最终包含在网络线程上接收到的数据。
使用 Platform.runLater() 更新 View 模型很简单,但它不适合我们的架构。
当前的架构将应用程序分为“ View ”和“网络/通信”部分。
- View 监听模型并更新相应的组件。不了解网络。
- 网络监听网络更新并相应地将数据写入模型。不了解 JavaFX。
所以我进退两难了。
要忠于架构和“关注点分离”——网络阅读器类不应调用
Platform.runLater()
为了保持简单,让网络阅读器类调用
Platform.runLater()
- 正常工作 - 无需额外代码。
我试图用代码来说明这一点
简单的方法
只需从网络阅读器调用 Platform.runLater()
public class SimpleUpdate extends Application {
private int clock;
public class Item {
private IntegerProperty x = new SimpleIntegerProperty(0);
public final IntegerProperty xProperty() {
return this.x;
}
public final int getX() {
return this.xProperty().get();
}
public final void setX(final int x) {
this.xProperty().set(x);
}
}
@Override
public void start(Stage primaryStage) throws Exception {
ObservableList<Item> viewModel = FXCollections.observableArrayList();
TableView<Item> table = new TableView<Item>(viewModel);
TableColumn<Item, Integer> colX = new TableColumn<>();
colX.setCellValueFactory(new PropertyValueFactory<Item, Integer>("x"));
table.getColumns().add(colX);
primaryStage.setScene(new Scene(table));
primaryStage.show();
new Thread(() -> {
while (true) {
Platform.runLater(() -> { // update on JavaFX thread
if (clock % 2 == 0) {
viewModel.add(new Item());
viewModel.add(new Item());
} else {
viewModel.remove(1);
}
for (Item each : viewModel) {
each.setX(each.getX() + 1);
}
clock++;
});
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Network update").start();
}
public static void main(String[] args) {
launch(args);
}
}
纯方法
- 网络读取器线程写入自己的模型
- View 监听器监听网络模型并使用 JavaFX 线程同步自己的 View 模型
- 要复杂得多
代码
public class PureUpdate extends Application {
private int clock;
public class Item {
private IntegerProperty x = new SimpleIntegerProperty(0);
public final IntegerProperty xProperty() {
return this.x;
}
public final int getX() {
return this.xProperty().get();
}
public final void setX(final int x) {
this.xProperty().set(x);
}
}
public class ViewItem extends Item {
private Item original;
public ViewItem(Item original) {
super();
this.original = original;
sync();
}
public void sync() {
setX(original.getX());
}
public Item getOriginal() {
return original;
}
}
@Override
public void start(Stage primaryStage) throws Exception {
ObservableList<ViewItem> viewModel = FXCollections.observableArrayList();
TableView<ViewItem> table = new TableView<ViewItem>(viewModel);
TableColumn<ViewItem, Integer> colX = new TableColumn<>();
colX.setCellValueFactory(new PropertyValueFactory<ViewItem, Integer>("x"));
table.getColumns().add(colX);
primaryStage.setScene(new Scene(table));
primaryStage.show();
ObservableList<Item> networkModel = FXCollections
.synchronizedObservableList(FXCollections.observableArrayList());
networkModel.addListener((Observable obs) -> {
Platform.runLater(() -> {
List<Item> alreadyKnown = new ArrayList<>();
for (Iterator<ViewItem> it = viewModel.iterator(); it.hasNext();) {
ViewItem each = it.next();
alreadyKnown.add(each.getOriginal());
if (networkModel.contains(each.getOriginal())) {
each.sync();
} else {
it.remove();
}
}
for (Item each : networkModel.toArray(new Item[0])) {
if (!alreadyKnown.contains(each)) {
viewModel.add(new ViewItem(each));
}
}
});
});
new Thread(() -> {
while (true) {
if (clock % 2 == 0) {
networkModel.add(new Item());
networkModel.add(new Item());
} else {
networkModel.remove(1);
}
for (Item each : networkModel) {
each.setX(each.getX() + 1);
}
clock++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Network update").start();
}
public static void main(String[] args) {
launch(args);
}
}
问题。我可以在不编写额外代码的情况下实现纯方法吗?
最佳答案
让处理器将通知发布到 Runnable
.或 Consumer<T>
,或定义自定义 @FunctionalInterface
.
这将使您在设计线程以及对 JavaFX 或任何其他需要在特定线程上同步的框架的依赖性时更容易进行测试。
使用消费者的例子:
public class NetworkReader {
private final Consumer<? super Data> consumer;
public NetworkReader(Consumer<? super Data> consumer) {
this.consumer = Objects.requireNonNull(consumer);
}
public void readStuff() {
while (...) {
Data data = ...;
consumer.accept(data);
}
}
}
NetworkReader
将用例如构建new NetworkReader(d -> Platform.runLater(() -> updateModel(d)));
当你想测试你可以通过做如下:
NetworkReader reader = new NetworkReader(d -> this.actuals = d);
reader.readStuff();
assertEquals(expecteds, actuals);
聪明的消费者可以在实际处理之前合并更新。
关于JavaFX 表更新线程架构不匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49969630/