JavaFX 多线程 - 连接线程不会更新 UI

标签 java multithreading javafx javafx-2 task

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

但因此,我需要 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 正常工作?

最佳答案

您永远不应该阻塞 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 来使其看起来更好。

关于JavaFX 多线程 - 连接线程不会更新 UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24578517/

相关文章:

java - 在 Java 中使用 Vector 并发崩溃

javafx:绑定(bind)未按预期工作

JavaFX CSS : How to inherit background color from other CSS settings?

java - 是否有允许在集群应用程序中订购事件的 API?

java - 如何根据请求类型使用Spring Security管理 session ?

java - executor.invokeAll() 未完成执行

java - Hibernate:如何返回map<String,List<Object>>

java - Scala:实现 Java 接口(interface)会导致类型不兼容

c# - 什么是 java Vector.element() C# 等价物

c++ - 返回是原子的,我应该在 getter 中使用临时的线程安全吗?