简体   繁体   English

MVC:观察目录变化的最佳方式

[英]MVC: Best way of watching a directory for changes

Context: 语境:
I am newbie at JavaFX, but I'm trying to build an application which one of it's basic feature is to show the user all folders in certain directory and automatically update the view when there are new folders or folders deleted in that directory. 我是JavaFX的新手,但我正在尝试构建一个应用程序,其中一个基本功能是向用户显示某个目录中的所有文件夹,并在该目录中删除新文件夹或文件夹时自动更新视图。 These folders can be considered as objects (in a ListView for example) and the user should be able to interact with them. 这些文件夹可以被视为对象(例如,在ListView中),用户应该能够与它们进行交互。 I want to build that application using MVC architecture. 我想使用MVC架构构建该应用程序。

What I've done so far: 到目前为止我做了什么:
So I have a view (fxml), a controller class and my model which handles my app logic. 所以我有一个视图(fxml),一个控制器类和我的模型来处理我的app逻辑。 I use the WatchDir example from Oracle as a helper class in my model and starting the WatchService in the controller like this: 我使用Oracle中的WatchDir 示例作为我模型中的辅助类,并在控制器中启动WatchService,如下所示:

@Override
public void initialize(URL location, ResourceBundle resources) {
    this.config = loadConfig(configFile);
    this.facade = new facade(config);
    Path rootPath = Paths.get(config.getDlRootPath());
    try {
        // register WatchService
        new WatchDir(rootPath, false).processEvents();
        statusText(rootPath + "is now being watched for changes");
    } catch (IOException e) {
        statusError("Directory " + e.getLocalizedMessage() + " does not exist.");
    }
}

In WatchDir method processEvents() I can do something like: 在WatchDir方法processEvents()中,我可以执行以下操作:

if (!recursive && (kind == ENTRY_CREATE)) {
  // new folder was created
}

My Question: 我的问题:
What is the best/most elegant way of telling my controller that something has changed and to update the ListView? 告诉我的控制器有什么变化并更新ListView的最佳/最优雅的方法是什么? I want to keep it MVC style. 我想保持MVC风格。

Different approach is also welcome :) 不同的方法也欢迎:)

The approach I would use would be to provide methods in WatchDir for registering callbacks. 我将使用的方法是在WatchDir提供用于注册回调的方法。 The easiest way to do this is just to use Consumer properties for the callbacks: 最简单的方法是使用Consumer属性进行回调:

public class WatchDir {

    private Consumer<Path> onCreate ;

    public void setOnCreate(Consumer<Path> onCreate) {
        this.onCreate = onCreate ;
    }

    // other callbacks as needed...

    // ...

    void processEvents() {

        // ...

        Path child = ... ; // file/folder that was created

        if (! recursive && kind == ENTRY_CREATE) {
            if (onCreate != null) {
                onCreate.accept(child);
            }
        }

        // ...
    }

    // ...
}

Note that WatchDir.processEvents() is a (non-terminating) blocking call, so you need to run it in a background thread. 请注意, WatchDir.processEvents()是一个(非终止)阻塞调用,因此您需要在后台线程中运行它。 So from your controller you do: 所以从你的控制器你做:

WatchDir watchDir = new WatchDir(rootPath, false) ;
watchDir.setOnCreate(path -> 
    Platform.runLater(() -> listView.getItems().add(path)));
Thread watchThread = new Thread(watchDir::processEvents);
watchThread.setDaemon(true);
watchThread.start();

Note that since the callback will be invoked on a background thread, updates to the UI should be wrapped in a Platform.runLater(...) . 请注意,由于将在后台线程上调用回调,因此UI的更新应包含在Platform.runLater(...) If you like, you can equip WatchDir with an Executor for executing callbacks, which would allow you to tell it just once to always execute them via Platform.runLater(...) : 如果您愿意,可以为WatchDir配备Executor来执行回调,这样您只需告诉它一次,即可通过Platform.runLater(...)执行它们:

public class WatchDir {

    // Executor for notifications: by default just run on the current thread
    private Executor notificationExecutor = Runnable::run ;
    public void setNotificationExecutor(Executor notificationExecutor) {
        this.notificationExecutor = notificationExecutor ;
    }

    private Consumer<Path> onCreate ;
    // etc...

    void processEvents() {

        // ...

        if (! recursive && kind == ENTRY_CREATE) {
            if (onCreate != null) {
                notificationExecutor.execute(() -> onCreate.accept(child));
            }
        }

        // ...
    }

}

and then 然后

WatchDir watchDir = new WatchDir(rootPath, false) ;
watchDir.setNotificationExecutor(Platform::runLater);
watchDir.setOnCreate(listView.getItems()::add); /* or path -> listView.getItems().add(path) */
Thread watchThread = new Thread(watchDir::processEvents);
watchThread.setDaemon(true);
watchThread.start();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM