Gui在使用线程时冻结

时间:2017-11-17 03:56:43

标签: multithreading javafx

我希望我的应用程序从数据库中自动刷新Vbox中的内容。我在initialize方法中启动了线程。为什么我的Gui冻结了。是否有更好的方法来执行GUI刷新的此类线程操作。

package Messanger.ChatWindow;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.text.TextAlignment;
import Messanger.Login.Login;
import java.io.IOException;
import Messanger.Settings.Settings;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javax.swing.JOptionPane;

public class Controller implements Initializable {

    Settings set = new Settings();

    VBox msg_vbox = new VBox();

    @FXML
    ScrollPane scrlpane;

    @FXML
    TextField message;

    protected Model md;

    public Controller() throws SQLException {
        this.md = new Model();
    }

    @FXML
    protected void Settings() {
        try {
            set.loadView();
        } catch (IOException ex) {
            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @FXML
    protected void Logout() throws IOException {
        Login lgin = new Login();
        lgin.loadView();
        ChatWindow.cW.close();
    }

    protected synchronized void refreshContent() throws SQLException {

        ResultSet messageArry = md.getMessages();

        while (messageArry.next()) {

            msg_vbox.getChildren().clear();

            //new label text with message.
            Label set_text = new Label();
            set_text.setText(messageArry.getString("username") + " Says: \n" + messageArry.getString("message"));
            set_text.setStyle("-fx-padding:10;"
                    + "-fx-width:100%;"
                    + "-fx-background-color:teal;"
                    + "    -fx-background-insets: 5;"
                    + "-fx-font-size:15;"
                    + "-fx-background-radius: 3;");

            set_text.setPrefSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);
            set_text.setWrapText(true);
            set_text.setTextAlignment(TextAlignment.JUSTIFY);
            set_text.setPrefWidth(600);

            //VBox wrapper
            msg_vbox.getChildren().addAll(set_text);
            msg_vbox.setPrefWidth(600);

            //Further wrapped by ScrollPane
            scrlpane.fitToHeightProperty();
            scrlpane.setContent(msg_vbox);
            scrlpane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
            scrlpane.vvalueProperty().bind(msg_vbox.heightProperty()); //sets the scroll view to new element.
        }
    }

    @FXML
    protected void sendMessage() {
        //new label text with message.
        Label set_text = new Label();
        set_text.setText(Messanger.Login.Controller.SESSION_usrname + " Says: \n" + message.getText());
        set_text.setStyle("-fx-padding:10;"
                + "-fx-width:100%;"
                + "-fx-background-color:teal;"
                + "    -fx-background-insets: 5;"
                + "-fx-font-size:15;"
                + "-fx-background-radius: 3;");

        set_text.setPrefSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);
        set_text.setWrapText(true);
        set_text.setTextAlignment(TextAlignment.JUSTIFY);
        set_text.setPrefWidth(600);

        //VBox wrapper
        msg_vbox.getChildren().addAll(set_text);
        msg_vbox.setPrefWidth(600);

        //Further wrapped by ScrollPane
        scrlpane.fitToHeightProperty();
        scrlpane.setContent(msg_vbox);
        scrlpane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        scrlpane.vvalueProperty().bind(msg_vbox.heightProperty()); //sets the scroll view to new element.
        message.setText("");
    }

    @FXML
    protected void check_key(KeyEvent ae) throws SQLException {
        if (ae.getCode().equals(KeyCode.ENTER)) {
            if (md.addMessage(Messanger.Login.Controller.SESSION_usrname, message.getText())) {
                sendMessage();
            } else {
                JOptionPane.showMessageDialog(null, "Message Sending failed \n "
                        + "Please Check Your Internet Connection", "Error ", JOptionPane.INFORMATION_MESSAGE);
            }
        }
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        scrlpane.setStyle("-fx-background:#32AED8");
        scrlpane.setPrefHeight(300);

        try {
            refreshContent();
        } catch (SQLException ex) {
            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
        }

        Service<Void> service = new Service<Void>() {
            @Override
            protected Task<Void> createTask() {
                return new Task<Void>() {
                    @Override
                    protected Void call() throws Exception {
                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                while (true) {
                                    try {
                                        refreshContent();
                                    } catch (SQLException ex) {
                                        Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
                                    }
                                    try {
                                        Thread.sleep(100);
                                    } catch (InterruptedException ex) {
                                        Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
                                    }

                                    System.out.println("asd");
                                }
                            }

                        });
                        return null;
                    }

                };
            }

        };

        service.start();
    }

} 

在代码中,我启动了线程,我想运行refreshContent方法。我也尝试过实现Runnable接口。但同样的问题也出现了。

1 个答案:

答案 0 :(得分:1)

您正在创建新主题。但是,从这个新线程中,您立即发布Runnable以在使用无限循环处理与DB连接的javaFX应用程序线程上运行,因此您将阻止应用程序线程。

要不阻止应用程序线程在另一个线程上执行任务的长时间运行部分,然后使用Platform.runLater更新UI。

您也可能不应该初始化结果集循环中的行...

private List<Node> refreshedContent() {
    List<Node> result = new ArrayList<>();

    ResultSet messageArry = md.getMessages();

     while (messageArry.next()) {
        // initialize nodes not yet attached to a scene
        Label set_text = new Label();
        set_text.setText(messageArry.getString("username") + " Says: \n" + messageArry.getString("message"));
        set_text.setStyle("-fx-padding:10;"
                + "-fx-width:100%;"
                + "-fx-background-color:teal;"
                + "    -fx-background-insets: 5;"
                + "-fx-font-size:15;"
                + "-fx-background-radius: 3;");

        set_text.setPrefSize(600, Region.USE_COMPUTED_SIZE);
        set_text.setWrapText(true);
        set_text.setTextAlignment(TextAlignment.JUSTIFY);

        result.add(set_text);
    }

    return result;
}

@Override
protected Void call() throws Exception {
    while (true) {
        // do long-running operation
        List<Node> newContent = refreshedContent();
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                // there should be no need to do this over and over again
                // you should move it outside of the task
                msg_vbox.setPrefWidth(600);
                //scrlpane.fitToHeightProperty(); // does nothing anyway...
                scrlpane.setContent(msg_vbox);
                scrlpane.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
                scrlpane.vvalueProperty().bind(msg_vbox.heightProperty()); //probably won't work the intended way...

                // update ui
                msg_vbox.getChildren().setAll(newContent);
            }

        });
        // do more long-running operations
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
            Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
        }

        System.out.println("asd");
    }
}

此外:

  • 考虑使用ListView
  • 通常,数据访问不是从UI层完成的。您可以从数据访问层执行更新,并使模型属性可观察并更新ui更改...
  • 尽量避免每秒多次重新创建节点。在这种情况下,ListView会有所帮助。如果您不想使用ListView,则应尝试尽可能多地重复使用现有Label,而不是使用新实例替换它们......