简体   繁体   中英

Java - Is it possible to use the Observer Pattern and threading in this way?

I hope someone here can help. I am just trying to wrap my head around the Observer Design Pattern, Threading and how I can use both for a project I am doing.

I currently need to implement the both of them on a Media Player I am building using Java FX.

I need to use both of them to update my listView(Populated by a getNames function of files in my directory. I need any changes to my folder of songs to reflect straight away on the GUI.

Is it possible, to have a running thread constantly calling my getNames function(returns an items variable), and if there are any changes to the items variable then I can use the Observer pattern to notify my GUI class to update its list.

I know it's possible to have a thread constantly hitting the function, but I just need some advice on if its then possible to use the Observer pattern to notify on if the items have changed!

I have no code to show, as I am still trying to figure out how to implement this.

Any ideas?

Appreciate any advice at all! Thanks :)

UPDATE

After quite a long time, Got this working with threads and observer patterm. Didn't need WatchService. Used my thread to constantly call a check for change method, then if method returned through then Observer kicked in to update GUI.

Its possible to use this pattern , you need to run a thread to keep watch on folder for files update and to make this thread safe use eventQueue to run your thread eg java.awt.EventQueue.invokeLater or invokeAndWait

Once change is detected by the thread, then your observer pattern will update GUI

Hope this helps!!

The best approach to this (IMO) would be:

  1. Consider this Oracle tutorial on the WatchService .
  2. As you are using JavaFX, wrap "the basic steps required to implement a watch service" from that tutorial in a JavaFX Task .
  3. Perhaps following the pattern from the Task javadoc "A Task Which Returns Partial Results" to feedback into your view any changes detected by the watch service.

As you note "unfortunately our lecturer won't let us use WatchService", then you can use a method like in the sample code below which is an active poll of the FileSystem. The use of the WatchService is definitely preferred as it can, internally within the JDK implementation, make use of OS provided file watch services. Those OS services can provide a notification of a file change event, so that the Java code does not need to actively poll the file system for changes. Nevertheless, the following inefficient job will likely suffice to do the job in this case...

What the code does is spawn a JavaFX task on a thread which polls the file system and modifies the observable list backing the ListView to match the files on the file system. The list modification is done within a Platform.runLater call to ensure that the modifications to the list backing the list view occur on the JavaFX application thread, so that the active scene graph is not modified off of the JavaFX application thread.

样本输出

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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