UI冻结即使使用任务& Platform.runLater(完整示例)

时间:2017-11-15 00:58:45

标签: java multithreading user-interface javafx task

我已经阅读了类似的问题但是当我向VBox添加许多节点时,我的UI仍然处于冻结状态。我在下面提供了一个功能齐全的程序,可以清楚地说明问题。

4秒后,ProgressIndicator冻结,因为5000个节点被添加到VBox。这是用于演示JavaFX线程冻结的过多数量,尽管使用Task(用于非UI工作)然后使用Platform.runLater()将节点添加到场景中。

在我的实际应用中,我没有添加空白TitlePane,而是通过TitlePane添加从FXML文件中获取的new FXMLLoader(),然后初始化loader.load()相关的控制器,它反过来初始化一些中等要求的计算 - 这些计算正在JavaFX线程上执行!因此,即使我正在添加更接近250个节点,但在最终使用Platform.runLater时,UI仍然会冻结。如何让ProgressIndicator冻结,直到显示红色背景?

完整示例:

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Accordion;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Timer;
import java.util.TimerTask;

public class FreezingUI extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        VBox mainBox = new VBox();
        mainBox.setPrefHeight(800);
        mainBox.setStyle("-fx-background-color: #f1f1f1; -fx-alignment: center");
        Label label = new Label();
        label.setMinHeight(50);
        label.setStyle("-fx-font-size: 24px; -fx-text-fill: #515151");

        ProgressIndicator progressIndicator = new ProgressIndicator(ProgressIndicator.INDETERMINATE_PROGRESS);
        mainBox.getChildren().addAll(progressIndicator, label);

        Scene scene = new Scene(mainBox, 500, 800);
        primaryStage.setScene(scene);
        primaryStage.show();

        Timer timer = new Timer();
        TimerTask task = new TimerTask(){
            private int i = 4;
            public void run(){
                if (i >= 0) {
                    Platform.runLater(()->{
                        label.setText("Freezing in " + i--);
                    });
                }else{
                    addNodesToUI(mainBox);
                    timer.cancel();
                }
            }
        };
        timer.scheduleAtFixedRate(task, 0, 1000);
    }

    private void addNodesToUI(VBox mainBox) {
        final int[] i = {0};
        Platform.runLater(() -> {
            Accordion temp = new Accordion();
            mainBox.getChildren().add(temp);
            while (i[0] < 5000) {

                TitledPane tp = new TitledPane();
                tp.setPrefWidth(300);
                tp.setPrefHeight(12);
                tp.setPadding(new Insets(10));
                tp.setStyle("-fx-background-color: red;");
                temp.getPanes().add(tp);
                i[0]++;

            }

        });
    }
}

1 个答案:

答案 0 :(得分:2)

这种情况正在发生,因为您要求UI线程在一个大块中完成大量工作。在创建所有5000个节点并将其添加到场景之前,UI线程无法退出while循环。

private void addNodesToUI(VBox mainBox) {
    final int[] i = {0};

    Accordion temp = new Accordion();

    Platform.runLater(() -> {
        mainBox.getChildren().add(temp);
    });

    while (i[0] < 5000) {
        TitledPane tp = new TitledPane();
        tp.setPrefWidth(300);
        tp.setPrefHeight(12);
        tp.setPadding(new Insets(10));
        tp.setStyle("-fx-background-color: red;");

        i[0]++;

        Platform.runLater(() -> {
            temp.getPanes().add(tp);
        });
    }
}

这将允许您小批量创建节点。这样,UI线程可以在逐步添加节点时尝试呈现UI。

对于您的FXML案例,您可以在另一个线程中创建和加载FXML。将场景分支附加到场景中时,您只需要处于UI线程中。但是,我怀疑这只会减轻影响,因为你仍然会一次性附加一大块。