【问题标题】:Java - Is it possible to use the Observer Pattern and threading in this way?Java - 是否可以以这种方式使用观察者模式和线程?
【发布时间】:2017-10-12 22:58:05
【问题描述】:

我希望这里有人可以提供帮助。我只是想围绕观察者设计模式、线程以及如何在我正在做的项目中使用这两者。

我目前需要在使用 Java FX 构建的媒体播放器上实现这两者。

我需要同时使用它们来更新我的 listView(由我目录中文件的 getNames 函数填充。我需要对我的歌曲文件夹进行任何更改以立即反映在 GUI 上。

是否有可能让一个正在运行的线程不断调用我的 getNames 函数(返回一个 items 变量),如果 items 变量有任何更改,那么我可以使用观察者模式通知我的 GUI 类更新其列表.

我知道有可能有一个线程不断地访问该函数,但我只需要一些建议,看看是否可以使用观察者模式来通知项目是否已更改!

我没有要显示的代码,因为我仍在试图弄清楚如何实现它。

有什么想法吗?

感谢任何建议!谢谢:)

更新

经过相当长的一段时间后,得到了这个使用线程和观察者模式的工作。不需要WatchService。使用我的线程不断调用检查更改方法,然后如果方法返回,则观察者启动以更新 GUI。

【问题讨论】:

  • 是什么让您认为这不可能?
  • 这是可能的,但繁琐且容易出错;考虑SwingWorker 来同步访问线程间共享的数据。
  • 我真的很纠结如何让 Observor 检测到文件更改并更改我的 listView。
  • 感谢@trashgod,但不幸的是,对于我们必须使用 Observor 和线程来获取标记的项目,我与这种方法有关。
  • java.nio.file.WatchService?

标签: java multithreading user-interface javafx observer-pattern


【解决方案1】:

可以使用此模式,您需要运行一个线程来监视文件夹以进行文件更新,并使此线程安全使用 eventQueue 来运行您的线程 例如 java.awt.EventQueue.invokeLater 或 invokeAndWait

一旦线程检测到更改,您的观察者模式将更新 GUI

希望这会有所帮助!

【讨论】:

    【解决方案2】:

    解决这个问题(IMO)的最佳方法是:

    1. 考虑WatchService 上的这个Oracle 教程。
    2. 当您使用 JavaFX 时,将该教程中的“实现监视服务所需的基本步骤”封装在 JavaFX Task 中。
    3. 也许可以按照任务 javadoc“返回部分结果的任务”中的模式将监视服务检测到的任何更改反馈到您的视图中。

    正如您所说的“不幸的是,我们的讲师不允许我们使用 WatchService”,那么您可以使用下面示例代码中的方法,它是 FileSystem 的主动轮询。 WatchService 的使用绝对是首选,因为它可以在 JDK 实现内部使用 OS 提供的文件监视服务。这些操作系统服务可以提供文件更改事件的通知,因此 Java 代码不需要主动轮询文件系统以进行更改。然而,在这种情况下,以下低效的工作可能足以完成这项工作......

    代码所做的是在一个线程上生成一个 JavaFX 任务,该线程轮询文件系统并修改支持 ListView 的可观察列表以匹配文件系统上的文件。列表修改是在 Platform.runLater 调用中完成的,以确保对支持列表视图的列表的修改发生在 JavaFX 应用程序线程上,因此活动场景图不会在 JavaFX 应用程序线程之外被修改。

    import javafx.application.*;
    import javafx.collections.*;
    import javafx.collections.transformation.SortedList;
    import javafx.concurrent.Task;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.stage.Stage;
    
    import java.io.File;
    import java.nio.file.*;
    import java.util.Arrays;
    import java.util.Comparator;
    
    public class FileWatcher extends Application {
    
        private static final Path WATCH_DIR = Paths.get(System.getProperty("user.dir"));
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage stage) {
            ObservableList<File> songFileList = FXCollections.observableArrayList();
            SortedList<File> sortedSongFileList = new SortedList<>(
                    songFileList,
                    Comparator.comparing(File::getName)
            );
    
            ListView<File> songListView = new ListView<>();
            songListView.setItems(sortedSongFileList);
            songListView.setCellFactory(param -> new ListCell<File>() {
                @Override
                protected void updateItem(File item, boolean empty) {
                    super.updateItem(item, empty);
    
                    if (item == null || empty) {
                        setText(null);
                        return;
                    }
    
                    setText(item.getName());
                }
            });
    
            SongWatcher watcher = new SongWatcher(
                    WATCH_DIR, songFileList
            );
            Thread watcherThread = new Thread(watcher, "song-watcher");
            watcherThread.setDaemon(true);
            watcherThread.start();
    
            Scene scene = new Scene(songListView);
            stage.setScene(scene);
            stage.show();
        }
    
        class SongWatcher extends Task<Void> {
            private static final String SONG_EXTENSION = "mp3";
            private static final long POLL_INTERVAL_MILLIS = 200;
    
            private final Path directory;
            private final ObservableList<File> songFiles;
    
            SongWatcher(Path directory, ObservableList<File> songFiles) {
                this.directory = directory;
                this.songFiles = songFiles;
            }
    
            @Override
            protected Void call() {
                System.out.println("Started watching " + directory + " for song file changes.");
    
                while (!isCancelled()) {
                    try {
                        Thread.sleep(POLL_INTERVAL_MILLIS);
                    } catch (InterruptedException e) {
                        if (isCancelled()) {
                            break;
                        }
    
                        Thread.currentThread().interrupt();
                    }
    
                    try {
                        if (!Files.isDirectory(directory)) {
                            throw new Exception("Watched directory " + directory + " is not a directory.");
                        }
    
                        File[] foundFiles =
                                directory
                                        .toFile()
                                        .listFiles(
                                                (dir, name) -> name.endsWith(SONG_EXTENSION)
                                        );
    
                        if (foundFiles == null) {
                            throw new Exception("Watched directory " + directory + " find files returned null (this is not expected).");
                        }
    
                        Platform.runLater(() -> {
                            // remove files from the song list which are no longer on the disk.
                            songFiles.removeIf(checkedFile ->
                                    Arrays.binarySearch(foundFiles, checkedFile) < 0
                            );
    
                            // add any files which are on the disk which are not in the song list.
                            for (File file: foundFiles) {
                                if (!songFiles.contains(file)) {
                                    songFiles.add(file);
                                }
                            }
                        });
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
    
                return null;
            }
    
            @Override
            protected void succeeded() {
                System.out.println("Stopped watching " + directory + " for song file changes.");
            }
    
            @Override
            protected void cancelled() {
                System.out.println("Cancelled watching " + directory + " for song file changes.");
            }
    
            @Override
            protected void failed() {
                System.out.println("Failed watching " + directory + " for song file changes.");
                if (getException() != null) {
                    getException().printStackTrace();
                }
            }
        }
    }
    

    【讨论】:

    • 非常感谢您的详细回答,非常感谢。几乎让线程和观察者一起工作。
    猜你喜欢
    • 2013-03-05
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 2015-12-31
    • 2012-02-03
    • 1970-01-01
    相关资源
    最近更新 更多