Обновить окно JavaFX в фоновом режиме - PullRequest
0 голосов
/ 06 мая 2018

В настоящее время я работаю над проводником файлов. Я хочу реализовать функцию поиска, которая отображает все соответствующие файлы в текущем каталоге и всех подкаталогах. Я попробовал это с помощью команды shell 'find', но это не сработало. После этого я попытался использовать функцию Files.walk (...) Java 8 API. Это сработало частично. Теперь я могу получить все файлы и распечатать их. Единственная проблема заключается в том, что я не могу добавить панель в моем графическом интерфейсе для каждого найденного файла.

Я пробовал это с одной задачей, но графический интерфейс не обновляется. В финальной версии, ниже, есть две темы. Тот, который получает файлы и заполняет их в очередь, а другие опрашивают объекты из очереди и отображают их в графическом интерфейсе. Из отладки я знаю, что очередь всегда имеет размер от 0 до 2. Это означает, что моя система работает. Но графический интерфейс по-прежнему не обновляется.

Может ли кто-нибудь помочь мне, пожалуйста?

public static final ConcurrentLinkedQueue<FilePane> files = new ConcurrentLinkedQueue<>();

public void search(String key) {
        Task<Boolean> listLoader = new Task<Boolean>() {
        {
            setOnSucceeded(workerStateEvent -> {
                System.out.println("Done");
            });

            setOnFailed(workerStateEvent -> getException().printStackTrace());
        }

        @Override
        protected Boolean call() throws Exception {
            try {
                Files.walkFileTree(Paths.get(currentDirectoryController().getDir().getAbsolutePath()), new HashSet<FileVisitOption>(Arrays.asList(FileVisitOption.FOLLOW_LINKS)),
                        Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
                            @Override
                            public FileVisitResult visitFile(Path file , BasicFileAttributes attrs) throws IOException {
                                return FileVisitResult.CONTINUE;
                            }

                            @Override
                            public FileVisitResult visitFileFailed(Path file , IOException e) throws IOException {
                                return FileVisitResult.SKIP_SUBTREE;
                            }

                            @Override
                            public FileVisitResult preVisitDirectory(Path dir , BasicFileAttributes attrs) throws IOException {
                                files.add(new FilePane(new KFile(dir.toFile()), cont));
                                return FileVisitResult.CONTINUE;
                            }
                        });
            } catch (IOException e) {
                e.printStackTrace();
            }

            return true;
        }
    };

    Thread loader = new Thread(listLoader);
    loader.setDaemon(true);
    loader.start();

    new Thread(() -> {
        Platform.runLater(() -> {
            while(true) {
                if(!files.isEmpty()) {
                    fileContainer.getChildren().add(files.poll());
                }
            }
        });
    }).start();
}

Ответы [ 2 ]

0 голосов
/ 06 мая 2018
new Thread(() -> {
    Platform.runLater(() -> {
        while(true) {
            if(!files.isEmpty()) {
                fileContainer.getChildren().add(files.poll());
            }
        }
    });
}).start();

Это просто запускает другой поток, который затем отправляет долгосрочную задачу (фактически бесконечный цикл) для запуска в потоке приложения.

Чтобы не блокировать ветку приложения, вам нужно публиковать только задачи, которые можно быстро выполнить в ветке приложения.

Дополнительные оптимизации:

  • Чтобы избежать публикации слишком большого количества задач в ветке приложения за короткое время, вы можете добавить задержку.
  • У вас должен быть способ закрыть второй поток. В противном случае поток, ищущий новые файлы, опубликованные первым потоком, будет работать вечно. Было бы неплохо выполнить синхронизацию самостоятельно, чтобы убедиться, что флаг, указывающий, что задача выполнена, не установлен на
  • Попробуйте использовать виртуальный элемент управления, например ListView или TableView, для отображения файлов. Это позволяет избежать создания огромного количества узлов в памяти, а также ускоряет компоновку, поскольку необходимо учитывать только видимые узлы.

На самом деле вы можете избежать сложности реализации потребителя / производителя, просто используя Platform.runLater из исходной задачи:

Task<Boolean> listLoader = new Task<Boolean>() {
    {
        setOnSucceeded(workerStateEvent -> {
            System.out.println("Done");
        });

        setOnFailed(workerStateEvent -> getException().printStackTrace());
    }

    @Override
    protected Boolean call() throws Exception {
        try {
            Files.walkFileTree(Paths.get(currentDirectoryController().getDir().getAbsolutePath()), new HashSet<FileVisitOption>(Arrays.asList(FileVisitOption.FOLLOW_LINKS)),
                    Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
                        @Override
                        public FileVisitResult visitFile(Path file , BasicFileAttributes attrs) throws IOException {
                            return FileVisitResult.CONTINUE;
                        }

                        @Override
                        public FileVisitResult visitFileFailed(Path file , IOException e) throws IOException {
                            return FileVisitResult.SKIP_SUBTREE;
                        }

                        @Override
                        public FileVisitResult preVisitDirectory(Path dir , BasicFileAttributes attrs) throws IOException {
                            FilePane filePane = new FilePane(new KFile(dir.toFile()), cont);
                            Platform.runLater(() -> fileContainer.getChildren().add(filePane)); // TODO bulk updates to prevent posting runables too frequently
                            return FileVisitResult.CONTINUE;
                        }
                    });
        } catch (IOException e) {
            e.printStackTrace();
        }

        return true;
    }
};

Thread loader = new Thread(listLoader);
loader.setDaemon(true);
loader.start();
0 голосов
/ 06 мая 2018
new Thread(() -> {
    Platform.runLater(() -> {
        while(true) {
            if(!files.isEmpty()) {
                fileContainer.getChildren().add(files.poll());
            }
        }
    });
}).start();

это утверждение действительно делает следующее:

Runnable action1 = () -> {
        while(true) {
            if(!files.isEmpty()) {
                fileContainer.getChildren().add(files.poll());
            }
        };

Runnable action2 = () -> {
    Platform.runLater(action1);
};

new Thread(action2).start();

Итак, action2 выполняется очень быстро, нет смысла запускать его в отдельном потоке. Однако action2 имеет бесконечный цикл и работает вечно и работает в потоке GUI, фактически блокируя этот поток и не позволяя ему перематывать экран.

Возможно, вы слышали, что вы не можете занимать поток GUI в течение длительного времени. То есть вы должны выполнить цикл вне потока GUI, примерно так:

Runnable action1 = () -> {
        while(true) {
            Platform.runLater(
                fileContainer.getChildren().add(files.take());
            });
        };

new Thread(action1).start();

Теперь мы видим, что этот поток делает очень мало - отбирает области файлов из очереди, оборачивает их простым способом и помещает во входную очередь потока GUI. Таким образом, это может быть устранено, и вся работа может быть выполнена в потоке загрузчика: вместо files.add(new FilePane(...)) do

 Platform.runLater(
     fileContainer.getChildren().add(new FilePane(...));
 });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...