简体   繁体   English

Platform.runLater() 如何发挥作用?

[英]How does Platform.runLater() function?

I have a simple app which updates the data in the background and while it updates, it disables all the other buttons and enables a TextArea to show the progress.我有一个简单的应用程序,它在后台更新数据,当它更新时,它会禁用所有其他按钮并启用 TextArea 来显示进度。

Steps:脚步:

  1. Disable all the other buttons in the mainUI (Button name: plotButton)禁用 mainUI 中的所有其他按钮(按钮名称:plotButton)

  2. Enable a TextArea showing that the updating has started (TextArea name: infoLogTextArea)启用显示更新已开始的 TextArea(TextArea 名称:infoLogTextArea)

  3. Then only start the update method (update() throws Exceptions).然后只启动更新方法(update() 抛出异常)。

Here is the code:这是代码:

@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");
        }
    }

Now the code works well but sometimes steps 1 and 2 are not executed and it starts step 3 (updating) which can freeze the program.现在代码运行良好,但有时第 1 步和第 2 步没有执行,它开始第 3 步(更新),这会冻结程序。 If I put Thread.sleep(10 secs) in between step 2 and 3 then it works completely fine .如果我将 Thread.sleep(10 secs) 放在第 2 步和第 3 步之间,则它完全正常 (it is commented in the code) (它在代码中注释)

But can anybody explain what is going on behind the scenes and why Platform.runLater() doesn't work all the time?但是有人能解释一下幕后发生了什么以及为什么Platform.runLater()不能一直工作吗?

A JavaFX application runs on the Application thread, which handles all the UI elements. JavaFX 应用程序在应用程序线程上运行,该线程处理所有 UI 元素。 This means that if you click Button A and clicking that button starts method A that takes 5 seconds to complete, and then one second after clicking that button, you try to click Button B which starts method B, method B won't start until method A finishes.这意味着如果您单击按钮 A 并单击该按钮启动需要 5 秒才能完成的方法 A,然后单击该按钮一秒钟后,您尝试单击启动方法 B 的按钮 B,方法 B 将不会启动,直到方法一个结束。 Or possibly Button B won't even work until method A finishes, I'm a little fuzzy on the detail there.或者按钮 B 可能在方法 A 完成之前甚至无法工作,我对那里的细节有点模糊。

A good way to stop your application from freezing is to use Threads.阻止应用程序冻结的一个好方法是使用线程。 To fix the above scenario, clicking Button A will start method A that starts a new Thread.要解决上述情况,单击按钮 A 将启动启动新线程的方法 A。 Then the Thread can take as long as it wants to complete without locking up the UI and preventing you from clicking Button B.然后线程可以在不锁定 UI 并阻止您单击按钮 B 的情况下完成它想要完成的时间。

Now, say something in method A needed to be on the application thread, for example, it updated a UI component, like a Label or a TextField.现在,说一些需要在应用程序线程上的方法 A 中的内容,例如,它更新了一个 UI 组件,如标签或文本字段。 Then inside your Thread in Method A you would need to put the part that affects the UI into a Platform.runLater() , so that it will run on the Application Thread with the rest of the UI.然后在方法 A 的线程中,您需要将影响 UI 的部分放入Platform.runLater() ,以便它与 UI 的其余部分一起在应用程序线程上运行。

What this means for your example is that you have two options.对于您的示例,这意味着您有两个选择。
1. Don't use threads at all, since you don't want the user to be interacting with the UI while the updates are happening anyway. 1. 根本不要使用线程,因为您不希望用户在更新发生时与 UI 交互。
2. move c.updateData() out of the Platform.runLater() like this: 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);
                        }
                    }
                });
            }
        };

Either one of those will work, but what you're doing right now is you're on the application thread, and then you start another thread whose only purpose is to run something on the application thread.其中任何一个都可以工作,但您现在所做的是在应用程序线程上,然后启动另一个线程,其唯一目的是在应用程序线程上运行某些内容。

The documentation of the Platform class explain everything very well : Platform 类的文档很好地解释了一切:

public static void runLater(Runnable runnable)

Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future .在将来某个未指定的时间JavaFX 应用程序线程上运行指定的 Runnable。 This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller.这个方法可以从任何线程调用,它将 Runnable 发布到一个事件队列,然后立即返回给调用者。 The Runnables are executed in the order they are posted. Runnables 按照它们发布的顺序执行。 A runnable passed into the runLater method will be executed before any Runnable passed into a subsequent call to runLater.传入 runLater 方法的 runnable 将在任何传入 runLater 的 Runnable 之前执行。 If this method is called after the JavaFX runtime has been shutdown, the call will be ignored: the Runnable will not be executed and no exception will be thrown.如果在 JavaFX 运行时关闭后调用此方法,则该调用将被忽略:不会执行 Runnable 并且不会抛出异常。 NOTE : applications should avoid flooding JavaFX with too many pending Runnables.注意:应用程序应该避免用太多挂起的 Runnables 淹没 JavaFX。 Otherwise, the application may become unresponsive .否则,应用程序可能会变得无响应 Applications are encouraged to batch up multiple operations into fewer runLater calls.鼓励应用程序将多个操作批处理为更少的 runLater 调用。 Additionally , long-running operations should be done on a background thread where possible, freeing up the JavaFX Application Thread for GUI operations.此外,长时间运行的操作应尽可能在后台线程上完成,从而释放 JavaFX 应用程序线程用于 GUI 操作。

This method must not be called before the FX runtime has been initialized.在初始化 FX 运行时之前不得调用此方法。 For standard JavaFX applications that extend Application, and use either the Java launcher or one of the launch methods in the Application class to launch the application, the FX runtime is initialized by the launcher before the Application class is loaded.对于扩展 Application 并使用 Java 启动器或 Application 类中的启动方法之一来启动应用程序的标准 JavaFX 应用程序,FX 运行时由启动器在加载 Application 类之前初始化。

So use the runLater to only update any UI elements on a non JavaFX thread and leave any heavy job to sit on the background thread.因此,使用 runLater 仅更新非 JavaFX 线程上的任何 UI 元素,并将任何繁重的工作留给后台线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM