从后台线程更新可观察列表的正确方法

2024-05-04

我正在尝试遵循 MVC 进行测试项目,因此我的模型应该完全独立于我的观点,但是我不确定应该如何更新在后台线程中更新的可观察列表(正在给出有关上传的字符串)文件),以便消息显示在 UI 上的 ListView 中。

我正在使用 JavaFX 并尝试使我的程序尽可能松散耦合。目前,视图包中的 GUI 取决于我的模型使用 Platform.runLater(...) 更新列表的事实 - 据我所知,我的模型应该完全独立于视图工作,并且应该'不必符合视图的需求。

现在下面的代码实际上“按预期工作”,只是没有正确建模,而且我不确定如何正确建模。一些初步研究表明我可能必须使用 Observer 和 observable - 并在中间有另一个类作为我的可观察列表 - 但我不确定如何设置它。

所以我有一个在后台线程上更新的可观察列表:

private ObservableList<String> transferMessages;

public FTPUtil(String host, int port, String user, String pass) {
    this.host = host;
    this.port = port;
    this.username = user;
    this.password = pass;       

    transferMessages = FXCollections.observableArrayList();

    connect();
}

public void upload(File src) {
    System.out.println("Uploading: " + src.getName());
    try {
        if (src.isDirectory()) {            
            ftpClient.makeDirectory(src.getName());
            ftpClient.changeWorkingDirectory(src.getName());
            for (File file : src.listFiles()) {
                upload(file);
            }
            ftpClient.changeToParentDirectory();
        } else {
            InputStream srcStream = null;
            try {
                addMessage("Uploading: " + src.getName());
                srcStream = src.toURI().toURL().openStream();
                ftpClient.storeFile(src.getName(), srcStream);
                addMessage("Uploaded: " + src.getName() + " - Successfully.");

            } catch (Exception ex) {
                System.out.println(ex);
                addMessage("Error Uploading: " + src.getName() + " - Speak to Administrator.");
            }
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

private void addMessage(String message){

    Platform.runLater(() -> transferMessages.add(0, message));

}

FTPUtil 类是我的模型。

我还有一个模型管理器类,它控制这个 FTPUtil 类:

public class ModelManager {

private ObservableList<String> fileAndFolderLocations;


private FTPUtil ftpUtil;

public ModelManager(String host, int port, String user, String pass) {

    ftpUtil = new FTPUtil(host, port, user, pass);
    fileAndFolderLocations = FXCollections.observableArrayList();

}

public boolean startBackup() {

    Task task = new Task() {
        @Override
        protected Object call() throws Exception {

            System.out.println("I started");
            ftpUtil.clearMessages();

            for(String location : fileAndFolderLocations){
                File localDirPath = new File(location);         
                ftpUtil.upload(localDirPath);
            }               
            return null;
        }           
    };      
    new Thread(task).start();

    return true;
}

public void addFileOrFolder(String fileOrFolder){
    if(!fileAndFolderLocations.contains(fileOrFolder)){
        fileAndFolderLocations.add(fileOrFolder);
    }       
}

public boolean removeFileOrFolder(String fileOrFolder){
    return fileAndFolderLocations.remove(fileOrFolder);
}

public ObservableList<String> getFilesAndFoldersList() {
    return fileAndFolderLocations;
}

public ObservableList<String> getMessages() {
    return ftpUtil.getMessages();
}

}

最后是我的图形用户界面:

public class BackupController {

private Main main;
private ModelManager mm;

@FXML
private ListView<String> messagesList;

@FXML
void forceBackup(ActionEvent event) {
    mm.startBackup();           

}

public void initController(Main main, ModelManager mm) {
    this.main = main;
    this.mm = mm;

    messagesList.setItems(mm.getMessages());
}


}

基本设置:

  • 不要在模型中使用 Platform.runLater
  • 不要将模型/经理的消息列表直接设置为 listView 的项目
  • 保留一个单独的可观察项目列表并将其设置为列表
  • 在管理器列表上安装一个监听器,使项目与消息保持同步:将这些修改包装在 Platform.runLater 中

一个非常原始的片段来说明设置:

private Parent getContent() {
    ModelManager manager = new ModelManager();
    ObservableList<String> uploading = FXCollections.observableArrayList("one", "two", "three");

    ObservableList<String> items = FXCollections.observableArrayList();
    manager.getMessages().addListener((ListChangeListener) c -> {

        while (c.next()) {
            if (c.wasAdded()) {
                Platform.runLater(() ->  
                    items.addAll(c.getFrom(), c.getAddedSubList()));
            } 
            if (c.wasRemoved()) {
                Platform.runLater(() ->
                     items.removeAll(c.getRemoved()));
            }
        }
    });


    ListView<String> list = new ListView<>(items);
    Button button = new Button("start");
    button.setOnAction(ev -> {
        uploading.stream().forEach(e -> manager.addFile(e));
        manager.startBackup();
    });
    BorderPane pane = new BorderPane(list);
    pane.setBottom(button);
    return pane;
}

@Override
public void start(Stage stage) throws Exception {
    Scene scene = new Scene(getContent());
    stage.setScene(scene);
    stage.show();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从后台线程更新可观察列表的正确方法 的相关文章

  • MongoTemplate upsert - 从 pojo 进行更新的简单方法(哪个用户已编辑)?

    这是一个简单的 pojo public class Description private String code private String name private String norwegian private String en
  • Jackson - 反序列化嵌套 JSON

    我有一个 JSON 字符串 其格式如下 response execution status ready report cache hit true created on 2013 07 29 08 42 42 fact cache erro
  • jvm 次要版本与编译器次要版本

    当运行使用具有相同主要版本但次要版本高于 JVM 的 JDK 编译的类时 JVM 会抛出异常吗 JDK 版本并不重要 类文件格式版本 http blogs oracle com darcy entry source target class
  • 使用 Java 在 WebDriver 中按 Ctrl+F5 刷新浏览器

    我已经使用 java 刷新了 WebDriver 中的浏览器 代码如下 driver navigate refresh 如何使用 Java 在 WebDriver 中按 Ctrl F5 来做到这一点 我认为您可以使用 WebDriver 和
  • URL.setURLStreamHandlerFactory

    我正在使用带有嵌入式 Jetty 的可执行 jar 开发一个 Web 应用程序 我的jar包含一个依赖jar jar in jar 我参考了JarRsrcLoader and RsrcURLStreamHandlerFactory由 Ecl
  • 如何开始使用 Chainsaw for Log4j?

    我想开始使用 Chainsaw v2 几乎没有关于它的信息 我只找到了this http www velocityreviews com forums t140105 help using chainsaw for log4j html 但
  • 尝试使用 JRI 将 R 与我的 Java 应用程序集成,但出现错误。谁能解释一下原因和解决办法吗?

    我需要将 Java 与 R 集成来运行一些数学命令并使用 R 的功能进行绘图 以下部分代码给出了错误 public static void main String args HelloRWorld r new HelloRWorld r h
  • 有多少种方法可以将位图转换为字符串,反之亦然?

    在我的应用程序中 我想以字符串的形式将位图图像发送到服务器 我想知道有多少种方法可以将位图转换为字符串 现在我使用 Base64 格式进行编码和解码 它需要更多的内存 是否有其他可能性以不同的方式做同样的事情 从而消耗更少的内存 现在我正在
  • 容器中的 JVM 计算处理器错误?

    最近我又做了一些研究 偶然发现了这一点 在向 OpenJDK 团队抱怨之前 我想看看是否有其他人观察到这一点 或者不同意我的结论 因此 众所周知 JVM 长期以来忽略了应用于 cgroup 的内存限制 众所周知 现在从 Java 8 更新某
  • 所有平台上的java

    如果您想用 java 为 Windows Mac 和 Linux 编写桌面应用程序 那么所有这些代码都相同吗 您只需更改 GUI 即可使 Windows 应用程序更像 Windows 等等 如果不深入细节 它是如何工作的 Java 的卖点之
  • 线程“main”中的异常 java.lang.StackOverflowError

    我有一段代码 但我无法弄清楚为什么它在线程 main java lang StackOverflowError 中给出异常 这是问题 Given a positive integer n prints out the sum of the
  • Intellij 中的 Google OR-Tools:UnsatisfiedLinkError

    我正在建立一个应该使用 Google OR Tools 的 java 框架 下面的代码编译成功 但在运行时抛出异常 Exception in thread main java lang UnsatisfiedLinkError com go
  • 异步迭代器

    我有以下代码 while slowIterator hasNext performLengthTask slowIterator next 由于迭代器和任务都很慢 因此将它们放入单独的线程中是有意义的 这是对迭代器包装器的快速而肮脏的尝试
  • 改变for循环的顺序?

    我遇到一种情况 我需要根据用户输入以不同的顺序循环遍历 xyz 坐标 所以我是 3D 空间中的一个区域 然后是一组像这样的 for 循环 for int x 0 x lt build getWidth x for int y 0 y lt
  • 使用 secp256r1 曲线和 SHA256 算法生成 ECDSA 签名 - BouncyCastle

    我正在尝试使用带有 secp256r1 曲线 P256 的 ECDSA 和用于消息哈希的 SHA256 算法生成签名 我也在使用 Bouncy Castle 库 下面的代码 public class MyTest param args pu
  • 如何初始化静态地图?

    你会如何初始化静态Map在Java中 方法一 静态初始化方法二 实例初始化 匿名子类 或者 还有其他方法吗 各自的优点和缺点是什么 这是说明这两种方法的示例 import java util HashMap import java util
  • 从库中捕获主线程 SynchronizationContext 或 Dispatcher

    我有一个 C 库 希望能够将工作发送 发布到 主 ui 线程 如果存在 该库可供以下人员使用 一个winforms应用程序 本机应用程序 带 UI 控制台应用程序 没有 UI 在库中 我想在初始化期间捕获一些东西 Synchronizati
  • 如何解决 PDFBox 没有 unicode 映射错误?

    我有一个现有的 PDF 文件 我想使用 python 脚本将其转换为 Excel 文件 目前正在使用PDFBox 但是存在多个类似以下错误 org apache pdfbox pdmodel font PDType0Font toUnico
  • 模拟pytest中的异常终止

    我的多线程应用程序遇到了一个错误 主线程的任何异常终止 例如 未捕获的异常或某些信号 都会导致其他线程之一死锁 并阻止进程干净退出 我解决了这个问题 但我想添加一个测试来防止回归 但是 我不知道如何在 pytest 中模拟异常终止 如果我只
  • 如何捕获 try-with-resource 语句中 close 方法抛出的异常

    我正在读关于try with resourceJava 中的语句可用于指定任意数量的资源 try Resource1 res1 initialize code Resource1 res2 initialize code statement

随机推荐