Platform.runLater()如何运作?

时间:2017-11-08 15:39:05

标签: java multithreading javafx runnable

我有一个简单的应用程序可以更新Backgroud中的数据,当它更新时会禁用所有其他按钮,并使TextArea显示进度。

步骤 -

  1. 禁用mainUI中的所有其他按钮(按钮名称:plotButton)

  2. 启用TextArea以显示更新已开始(TextArea name:infoLogTextArea)

  3. 然后只启动更新方法(update()抛出异常)。

  4. 以下是以下代码 -

    @FXML
        public void handleUpdateButton() {
    
            infoLogTextArea.setVisible(true);
            infoLogTextArea.appendText("Please wait while downloading data from internet.....\n");      
            plotButton.setDisable(true);
            updateButton.setDisable(true);
    
            if(c!=null) {
                Runnable task = new Runnable() {
                    @Override
                    public void run() {
                        // Thread.sleep(10000); -> sleep for 10secs
                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    c.updateData();
                                    infoLogTextArea.appendText(c.getErrorLog().toString());
                                    plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
                                    infoLogTextArea.appendText("Successfully updated the data from Internet\n");
                                }catch (IOException e) {
                                    infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
                                }
                                finally {
                                    plotButton.setDisable(false);
                                    updateButton.setDisable(false);
                                }
                            }
                        });
                    }
                };
    
                new Thread(task).start();
    
            }else {
                System.out.println("c not initialized");
            }
        }
    

    现在代码运行良好,但有时步骤1和2没有执行,它启动步骤3(更新),可以冻结程序。 现在,如果我将Thread.sleep(10秒)置于步骤2和3之间,那么它将完全正常。 (在代码中注释)

    但是有人可以解释一下后面会发生什么,以及为什么platform.runLater()不能一直工作?

2 个答案:

答案 0 :(得分:3)

JavaFX应用程序在Application线程上运行,该线程处理所有UI元素。这意味着如果单击按钮A并单击该按钮启动方法A需要5秒钟才能完成,然后单击该按钮后一秒钟,您尝试单击按钮B启动方法B,方法B不会启动直到方法A结束。或者按钮B甚至可能无法工作直到方法A完成,我对那里的细节有点模糊。

阻止应用程序冻结的好方法是使用Threads。要解决上述问题,单击按钮A将启动启动新线程的方法A.然后线程可以在完成所需的时间内完成而不会锁定UI并阻止您单击按钮B.

现在,在方法A中说一些需要在应用程序线程上的东西,例如,它更新了一个UI组件,比如Label或TextField。然后在方法A中的线程内部,您需要将影响UI的部分放入Platform.runLater(),以便它将在应用程序线程上与其余UI一起运行。

这对你的例子意味着你有两个选择 1.根本不使用线程,因为您不希望用户在更新发生时与UI进行交互。
2.将c.updateData()移出Platform.runLater(),如下所示:

 Runnable task = new Runnable() {
            @Override
            public void run() {
                c.updateData();
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            infoLogTextArea.appendText(c.getErrorLog().toString());
                            plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
                            infoLogTextArea.appendText("Successfully updated the data from Internet\n");
                        }catch (IOException e) {
                            infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
                        }
                        finally {
                            plotButton.setDisable(false);
                            updateButton.setDisable(false);
                        }
                    }
                });
            }
        };

其中任何一个都可以工作,但是你现在正在做的是你在应用程序线程上,然后你启动另一个线程,其唯一目的是在应用程序线程上运行某些东西。

答案 1 :(得分:1)

Platform类的文档很好地解释了所有内容:

public static void runLater(Runnable runnable)
  

在JavaFX Application Thread上运行指定的Runnable   未指定时间未来。这种方法可以从中调用   任何线程,都会将Runnable发布到事件队列然后返回   立即给来电者。 Runnables按顺序执行   他们被张贴了。传递给runLater方法的runnable将是   在任何Runnable传入后续调用之前执行   runLater。如果在JavaFX运行时之后调用此方法   shutdown,将忽略该调用:Runnable将不会被执行   并且不会抛出异常。 注意:应用程序应避免   JavaFX充斥着太多未决的Runnables。否则,   应用程序可能会无响应。鼓励申请   将多个操作批处理为更少的runLater调用。的另外下,   长时间运行的操作应该在后台线程中完成   可能,为GUI操作释放JavaFX应用程序线程。

     

在FX运行时之前,不得调用此方法   初始化。对于扩展Application的标准JavaFX应用程序,   并使用Java启动器或其中一个启动方法   应用程序类启动应用程序,FX运行时是   在加载Application类之前由启动程序初始化。

因此,使用runLater只更新非JavaFX线程上的任何UI元素,并将任何繁重的工作留在后台线程上。