JavaFX 多线程 - 加入线程不会更新 UI

问题描述 投票:0回答:2

我正在尝试创建一个加载程序对话框,用户可以在其中知道程序正在加载所请求的内容并且程序正在按预期运行。

但因此,我需要

join()
解析器线程,然后继续主线程,这会使对话框空白。

ParserTask.java

public class ParserTask extends Task<Void>{
    @Override
    protected Void call() throws Exception {
        for(int i=0;i<10;i++){
            //parse stuff
            Thread.sleep(1000);
            updateProgress(i,9);
            updateMessage("foo no:"+ i);
        }
        updateMessage("done");
        return null;
    }

}

Main.java

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main  extends Application {

    @Override
    public void start(Stage stage) {
        Group root = new Group();
        Scene scene = new Scene(root, 300, 150);
        stage.setScene(scene);
        stage.setTitle("Progress Controls");
        Button btn = new Button("call the thread");
        btn.setOnAction(new EventHandler<ActionEvent>(){
            @Override
            public void handle(ActionEvent arg0) {
                threadDialog();
            }
        });
        VBox vb = new VBox();
        vb.getChildren().addAll(btn);
        scene.setRoot(vb);
        stage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
    private void threadDialog() {
        Stage stageDiag = new Stage();
        ParserTask pTask = new ParserTask();
        final Label loadingLabel = new Label("");
        loadingLabel.textProperty().bind(pTask.messageProperty());
        final ProgressBar pbar = new ProgressBar();
        pbar.setProgress(0);
        pbar.setPrefHeight(20);
        pbar.setPrefWidth(450);
        pbar.progressProperty().bind(pTask.progressProperty());
        GridPane grid = new GridPane();
        grid.setHgap(14.0);
        grid.add(loadingLabel, 0, 0);
        grid.add(pbar, 1, 1);
        Scene sceneDiag = new Scene(grid);
        stageDiag.setScene(sceneDiag);
        stageDiag.setTitle("Foo thread is loading");
        stageDiag.show();
        Thread parser = new Thread(pTask);
        parser.start();
        try {
            parser.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

如果,我评论/删除此代码:

        try {
            parser.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

然后,我可以看到线程按预期加载,否则,我只能看到处于最终状态的空白屏幕(最后一条消息和完整的进度条)。

    while(!loadingLabel.getText().contains("one")){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

甚至

while(parser.isAlive());
也没有成功。

我的问题是:如何等待我的线程并保持 UI 正常工作?

java multithreading javafx javafx-2 task
2个回答
4
投票

永远不应该阻塞 UI 线程。

后台处理的典型模式如下所示:

  1. 更改 UI 以指示后台处理正在进行中(显示进度条、禁用按钮等)
  2. 启动后台线程
  3. 在后台线程中,处理完成后,将UI改回来并显示处理结果。应使用
    Platform.runLater()
    调用执行此操作的代码(以及从后台线程与 UI 交互的任何其他代码)

简化示例:

btn.setOnAction(new EventHandler<ActionEvent>(){
    @Override
    public void handle(ActionEvent arg0) {
        // 1
        setBackgroundProcessing(true); 

        // 2
        new Thread() {
            public void run() {
                doProcessing();

                // 3
                Platform.runLater(new Runnable() {
                    public void run() {
                        displayResults();
                        setBackgroundProcessing(false);
                    }
                });
            }
        }.start();
    }
});

上面的代码不是很优雅,您可能需要使用线程池和/或

ListenableFuture
使其看起来更好。


0
投票

使用 Platform.runLater()

有两个基本使用规则:

  • 您不需要从 Runnable 返回任何变量
  • 你需要执行的Runnable很短

第二个特别重要,因为 JavaFX 正在平衡您的 Runnable 与其自己的工作负载。如果您使用密集的 Runnables 淹没 Platform.runLater(),界面可能会变得无响应。

可以看【参考链接】[1]

[1]: https://edencoding.com/force-refresh-scene/#:~:text=You%20can%20see%20JavaFX%20usually,bottom%20at%20about%2060%20Hz).

© www.soinside.com 2019 - 2024. All rights reserved.