正确的方法从后台线程更新Observable List [英] Correct Way to update Observable List from background thread

查看:843
本文介绍了正确的方法从后台线程更新Observable List的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试按照MVC进行测试项目,因此我的模型应该完全独立于我的视图,但是我不确定如何更新在后台线程中更新的Observable List(它正在给出关于通过FTP上传文件的字符串)以便消息在ListView中显示在UI上。

I'm trying to follow MVC for a test project, so my model should be completely independant from my view, however I'm not sure how I should update an Observable List which gets updated in a background thread (It's being given Strings about uploading files through FTP) so that the messages appear on the UI in a ListView.

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

I am using JavaFX and trying to get my program as loosely coupled as possible. At this current moment, the GUI in the view package is depending on the fact that my model updates my list using Platform.runLater(...) - which to my knowledge, my model should work completely independent from the view, and shouldn't have to conform to the View's needs.

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

Now the following code actually "works as intended" it's just not modelled correctly, and I'm not sure how I can model it correctly. Some initial research brought up that I might have to use Observer and observable - and have another class in the middle to act as my observable list - but I'm not sure how I would set this up.

所以我有一个Observable列表,它在后台线程上更新:

So I have an Observable list which is updated on a background thread:

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类是我的模型。

The FTPUtil class is my model.

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

I also have a Model Manager class which is what controls this FTPUtil class:

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();
}

}

最后是我的GUI:

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项目。

  • 保留单独的observable项目列表并将其设置为列表

  • 在管理器列表上安装一个监听器,使项目与消息保持同步:将这些修改包装在Platform.runLater中

  • do not use Platform.runLater in the model
  • do not set the model/manager's list of messages directly as items to the listView
  • do keep a separate observableList of items and set that to the list
  • install a listener on the manager's list that keeps the items in sync with the messages: wrap those modifications in Platform.runLater

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

A very raw snippet to illustrate the setup:

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();
}

这篇关于正确的方法从后台线程更新Observable List的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆