JavaFX 表更新线程架构不匹配

标签 java javafx

我有一个 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/

相关文章:

java - 单击按钮时更改 javafx webview 方向

java - 如何在 Stage.show() 之后运行函数?

java - 是否有 "best"方法从静态方法调用类方法?

java - ObservableList 不更新 ArrayList

JavaFX Web View : how do I change the default cursor?

c# - 如何在 Java、C# 和/或 C 中查找无线网络 (SSID) 的列表?

具有昂贵参数的Java同步方法

java - 创造比数独游戏更伟大的游戏板

java - 引用的属性不是 (One|Many)ToOne : in mappedBy of

java - 如何在调用 wicket ajax 提交按钮后调用 jquery 函数