首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何正确地线程化javafx警报/文件选择器等

如何正确地线程化javafx警报/文件选择器等
EN

Stack Overflow用户
提问于 2018-04-27 17:36:01
回答 2查看 1.4K关注 0票数 5

我在看这个问题JavaFX show dialogue after thread task is completed,但我的问题正好相反。当您需要从用户那里返回一些数据时,在文件回波器或警告之后,最好的方式是什么?

我现在拥有的是:

代码语言:javascript
复制
Platform.runLater(()->{
    File file = fileChooser.showOpenDialog(root.getScene().getWindow());
    if(file == null) {
        return;
    }
    executorService.execute(()->{
        //more code here which uses file
    });
});

其中executorService是之前创建的ExecutorService。我想我也可以很容易地使用Task或Thread或其他任何东西,但是如何线程关闭并不重要,只是因为它会锁定UI,所以我不希望在Application线程上发生这样的事情。

我知道这不是一个mvce,但我希望它说明了我在Platform.runLater调用中遇到的线程问题。

下面是一个极端的例子,说明这类事情有多复杂

代码语言:javascript
复制
@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
                            // ...
                        });
                    }
                });
            });
        });
    });
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-04-27 18:06:28

您的问题似乎是在后台任务的中间需要只有在JavaFX应用程序线程上才能检索的信息。使用answer,James_D给出的James_D非常适合这一点。我想提供一个替代方案:CompletableFuture (在Java8中添加)。

代码语言:javascript
复制
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
        }

    });
}

FutureTaskCompletableFuture都会为您工作。我更喜欢CompletableFuture,因为它提供了更多选项(如果需要的话),而且join()方法不会像get()那样抛出检查过的异常。然而,CompletableFuture是一个Future (就像FutureTask),所以您仍然可以在CompletableFuture中使用get()

票数 4
EN

Stack Overflow用户

发布于 2018-04-27 17:50:41

如果您需要对返回结果的UI线程执行一些操作,则创建一个FutureTask,将其提交给UI线程,然后在后台线程上等待它完成。这使您可以“扁平化”代码。

您还可以将Platform.runLater(...)抽象为一个Executor (毕竟,它只是执行Runnables的东西),这可以使它(也许)稍微干净一些。

通过将代码划分为较小的方法(通常只使用其他标准编程技术),可以使代码相当清晰。

下面是基本思想(您需要添加异常处理,或者创建一个Callable (可以抛出异常),而不是Runnable):

代码语言:javascript
复制
@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() {
    // ...
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50067565

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档