Как правильно скинуть ветку javafx Alerts / fileChooser и т. Д. - PullRequest
0 голосов
/ 27 апреля 2018

Я смотрел на этот вопрос JavaFX показать диалог после выполнения задачи потока , но мой вопрос вроде как противоположный. Каков наилучший способ срезать ветвь после выбора файла или оповещения, когда вам нужны данные от пользователя?

Вот что у меня сейчас:

Platform.runLater(()->{
    File file = fileChooser.showOpenDialog(root.getScene().getWindow());
    if(file == null) {
        return;
    }
    executorService.execute(()->{
        //more code here which uses file
    });
});

где executorService - это ExecutorService, который был сделан ранее. Я полагаю, что я мог бы так же легко использовать Задачу, Поток или что-то еще, но не имеет значения, как это связано, просто то, что это занимает некоторое время, и я не хочу, чтобы это происходило в потоке Приложения, потому что это заблокирует пользовательский интерфейс.

Я знаю, что это не mvce, но я надеюсь, что это демонстрирует проблему, с которой я сталкиваюсь с потоками внутри Platform.runLater вызовов.

Вот крайний пример того, насколько запутанными становятся такие вещи

@FXML
public void copyFiles(ActionEvent event){
    //this method is on the application thread because a button or something started it
    // so we thread off here
    executorService.execute(()->{
        // do some stuff
        // ...
        // get location to copy to from user
        // must happen on the application thread!
        Platform.runLater(()->{
            File file = fileChooser.showOpenDialog(root.getScene().getWindow());
            if(file == null) {
                return;
            }
            executorService.execute(()->{
                // more code here which uses file
                // ...
                // oh wait, some files have the same names! 
                // we need a user's confirmation before proceeding
                Platform.runLater(()->{
                    Alert alert = new Alert(AlertType.CONFIRMATION, "Do you want to overwrite files with the same names?", ButtonType.OK, ButtonType.CANCEL);
                    Optional<ButtonType> choice = alert.showAndWait();
                    if(choice.isPresent && choice.get == ButtonType.OK){
                        // do something, but not on the application thread
                        executorService.execute(()->{
                            // do the last of the copying
                            // ...
                        });
                    }
                });
            });
        });
    });
}

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

Кажется, вашей проблеме нужна информация в середине фоновой задачи, которую можно получить только в потоке приложения JavaFX. Ответ , данный James_D, отлично подходит для этого, используя FutureTask. Я хотел бы предложить альтернативу: CompletableFuture (добавлено в Java 8).

public void copyFiles(ActionEvent event) {

    executorService.execute(() -> {

        // This uses CompletableFuture.supplyAsync(Supplier, Executor)

        // need file from user
        File file = CompletableFuture.supplyAsync(() -> {
            // show FileChooser dialog and return result
        }, Platform::runLater).join(); // runs on FX thread and waits for result

        if (file == null) {
            return;
        }

        // do some stuff

        // ask for confirmation
        boolean confirmed = CompletableFuture.supplyAsync(() -> {
            // show alert and return result
        }, Platform::runLater).join(); // again, runs on FX thread and waits for result

        if (confirmed) {
            // do more stuff
        }

    });
}

Оба FutureTask и CompletableFuture будут работать для вас. Я предпочитаю CompletableFuture, потому что он предоставляет больше опций (при необходимости), а метод join() не генерирует проверенные исключения, как get(). Однако CompletableFuture - это Future (точно так же, как FutureTask), и поэтому вы все равно можете использовать get() с CompletableFuture.

0 голосов
/ 27 апреля 2018

Если вам нужно что-то сделать в потоке пользовательского интерфейса, который возвращает результат, создайте FutureTask, отправьте его в поток пользовательского интерфейса, а затем в фоновом потоке дождитесь его завершения. Это позволяет «сгладить» код.

Вы также можете абстрагировать Platform.runLater(...) как Executor (в конце концов, это просто то, что выполняет Runnable с), что может сделать его (возможно) немного чище.

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

Вот основная идея (вам нужно добавить обработку исключений или создать Callable (который может генерировать исключение) вместо Runnable):

@FXML
public void copyFiles(ActionEvent event){

    Executor uiExec = Platform::runLater ;

    //this method is on the application thread because a button or something started it
    // so we thread off here

    Callable<Void> backgroundTask = () -> {
        doFirstTimeConsumingThing();

        FutureTask<File> getUserFile = new FutureTask<>(this::getUserFile) ;
        uiExec.execute(getUserFile);
        File file = getUserFile.get();
        if (file == null) return null ;

        doAnotherTimeConsumingThing(file);
        FutureTask<Boolean> getUserConfirmation = new FutureTask<>(this::showConfirmation);
        uiExec.execute(getUserConfirmation);
        if (! getUserConfirmation.get()) return null ;

        doMoreTimeConsumingStuff();

        // etc...

        return null ;
    };
    executorService.execute(backgroundTask);
}

private File getUserFile() {
    return fileChooser.showOpenDialog(root.getScene().getWindow());
}

private Boolean getUserConfirmation() {
    Alert alert = new Alert(AlertType.CONFIRMATION, "Do you want to overwrite files with the same names?", ButtonType.OK, ButtonType.CANCEL);
    return alert.showAndWait()
        .filter(ButtonType.OK::equals)
        .isPresent();
}

private void doFirstTimeConsumingThing() {
    // ...
}

private void doAnotherTimeConsumingThing(File file) {
    // ....
}

private void doMoreTimeConsumingStuff() {
    // ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...