javafx防止ui冻结

nwsw7zdq  于 2021-06-30  发布在  Java
关注(0)|答案(3)|浏览(356)

我希望用户等到我的方法调用下载一个文件(可能需要2到3分钟)完成。我为方法调用创建了一个新线程,并在内部调用它 run() 方法。根据返回值,应该显示一条消息,不管是成功还是错误。因此我不得不使用 join() . 但这仍然冻结,因为ui仍在等待方法调用完成。我怎样才能克服这个问题(基本上,我需要使用一个线程使ui不冻结,同时,我希望根据方法的返回值显示一条消息)

btn.setOnAction(new EventHandler<ActionEvent>() {
   @Override
   public void handle(ActionEvent event) {
      processMsg.setText("Do not close. This may take few minutes...");
      Thread t1 = new Thread(new Runnable(){
         @Override
         public void run() {
            try {
                   ret = /*Method-call-which-takes-long-time*/
                } catch (IOException ex) {
                     Logger.getLogger(JavaFXApplication1.class.getName()).log(Level.SEVERE, null, ex);
                } catch (SQLException ex) {
                     Logger.getLogger(JavaFXApplication1.class.getName()).log(Level.SEVERE, null, ex);
                }
         }
      });
      t1.start();
      try {
           t1.join();
      } catch (InterruptedException ex) {
           Logger.getLogger(JavaFXApplication1.class.getName()).log(Level.SEVERE, null, ex);
      }
      if (ret != null){
         if (ret.equals("TRUE")){
            pane.getChildren().remove(processMsg);
            processMsg.setText("File downloaded at location: "+ chosenDir.getAbsolutePath());
            pane.add(processMsg,0,11,2,1);
         }
         else{
            pane.getChildren().remove(processMsg);
            processMsg.setText(ret);
            pane.add(processMsg,0,11,2,1);
         }
      }
   }
});
uxh89sit

uxh89sit1#

您可能知道javafx呈现是由名为javafx application thread的线程处理的。您可以看到处于eclipse调试模式的线程。因此,如果使此线程等待另一个线程,则渲染将冻结。这就是这里发生的事情。

你可以做什么

单击此按钮时,禁用不希望用户单击的按钮。
使用容器轮询结果。这可以是一个全局队列,也可以使用rxjava优雅地实现 BehaviorSubject 或者其他合适的实现 Subject .
当耗时的方法完成时,它将用结果或调用填充队列 onNext() 在rxjava上 Subject .
当队列的侦听器或 Subject 获取值,然后重新启用按钮。
让我看看能不能快速地把样品拼凑起来。同时,你可以自己试一试。

y1aodyip

y1aodyip2#

根据返回值,应该显示一条消息,不管是成功还是错误。因此我不得不使用 join() .
这种假设是错误的。有几种方法可以从后台线程更新gui。 Platform.runLater 是最灵活的一种,但在这种情况下,我只建议使用 Task ,它允许您通过 onSucceeded 以及 onFailed 属性时调用的属性 call 逻辑完成时是否引发异常:

Task<MyReturnType> task = new Task<MyReturnType>() {

    @Override
    protected MyReturnType call() throws Exception {
        return someLongCalculation(); // may throw an exception
    }

};

task.setOnSucceeded(evt -> {
    // we're on the JavaFX application thread here
    MyReturnType result = task.getValue();
    label.setText(result.toString());

});

task.setOnFailed(evt -> {
    // we're on the JavaFX application thread here
    label.setText("Error: " + task.getException().getMessage());
});
new Thread(task).start(); // alternatively use ExecutorService
``` `Task.updateValue` ,  `Task.updateMessage` 以及 `Task.updateProgress` 允许您将可以使用 `value` ,  `message` 以及 `progress` 财产;这些属性在javafx应用程序线程上更新。
bhmjp9jg

bhmjp9jg3#

我建议您查看这个oracle教程,了解如何在ui组件之外处理业务逻辑。
另外,对于您的问题,使用completablefuture将对执行此类异步工作有很大帮助。
具体来说, whenComplete() 如果你在一个 CompletableFuture 然后再显示消息。
以下是whencomplete()的参考

相关问题