【问题标题】:How does Platform.runLater() function?Platform.runLater() 如何运作?
【发布时间】:2018-04-21 09:09:22
【问题描述】:

我有一个简单的应用程序,它在后台更新数据,当它更新时,它会禁用所有其他按钮并启用 TextArea 来显示进度。

步骤:

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

  2. 启用显示更新已开始的 TextArea(TextArea 名称:infoLogTextArea)

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

代码如下:

@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 secs) 放在第 2 步和第 3 步之间,那么它完全没问题。 (代码中有注释)

但是任何人都可以解释幕后发生的事情以及为什么Platform.runLater() 不能一直工作吗?

【问题讨论】:

  • 不是真正的直接重复,但看看这个答案:stackoverflow.com/questions/34985943/… 假设c.updateData() 需要很长时间,你应该把它放在runLater 调用之外。换句话说 - 仅在 Platform.runLater 中包装实际更改 UI 的调用。

标签: java multithreading javafx runnable


【解决方案1】:

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

阻止应用程序冻结的一个好方法是使用线程。要修复上述情况,单击按钮 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);
                        }
                    }
                });
            }
        };

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

【讨论】:

    【解决方案2】:

    Platform 类的文档很好地解释了一切:

    public static void runLater(Runnable runnable)
    

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

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

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

    【讨论】:

      猜你喜欢
      • 2015-05-31
      • 2019-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-17
      • 2017-01-31
      • 2012-11-26
      相关资源
      最近更新 更多