简体   繁体   English

使用线程时Gui冻结

[英]Gui freezes when using thread

I want my application to auto-refresh the content in the Vbox from the database. 我希望我的应用程序从数据库自动刷新Vbox中的内容。 I have started the thread in the initialize method. 我已经在initialize方法中启动了线程。 Why does my Gui freezes. 为什么我的桂冻结了。 Is there any better way to perform such threading operation for GUI refreshing. 有没有更好的方法来执行此类线程操作以进行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();
    }

} 

As in the code i've started the thread and i want to run the refreshContent method. 如代码中所示,我已经启动线程,并且我想运行refreshContent方法。 I've also tried it implementing the Runnable interface. 我也尝试过实现Runnable接口。 But same problem occurs. 但是发生同样的问题。

You are creating a new thread. 您正在创建一个新线程。 However from this new thread you immediately post a Runnable to be run on the javaFX application thread that is handling the connection to the DB using a infinite loop, so you are blocking the application thread. 但是,从这个新线程中,您会立即发布一个Runnable ,以便在使用无限循环处理与数据库的连接的javaFX应用程序线程上运行,因此您将阻塞应用程序线程。

To not block the application thread do the long-running parts of the task on a different thread, then use Platform.runLater to update the UI. 为了不阻塞应用程序线程,请在另一个线程上执行长时间运行的任务,然后使用Platform.runLater更新UI。

Also you probably shouldn't initialize the rows in the result set loop... 同样,您可能不应该在结果集循环中初始化行...

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

Furthermore: 此外:

  • Consider using ListView 考虑使用ListView
  • Usually data access is not done from the UI layer. 通常,数据访问不是从UI层完成的。 You could do the updates from a data access layer and make the model properties observable and update the ui on changes... 您可以从数据访问层进行更新,并使模型属性可观察并在更改时更新用户界面...
  • Try avoid recreating the nodes multiple times a second. 尝试避免每秒多次重新创建节点。 ListView would help in this case. 在这种情况下, ListView会有所帮助。 If you do not want to use a ListView you should try reusing existing Label s as much as possible instead of replacing them with new instances... 如果您不想使用ListView ,则应尝试尽可能重用现有的Label ,而不是用新的实例替换它们。

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

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