user7032676
user7032676

Reputation:

Gui freezes when using thread

I want my application to auto-refresh the content in the Vbox from the database. I have started the thread in the initialize method. Why does my Gui freezes. Is there any better way to perform such threading operation for GUI refreshing.

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. I've also tried it implementing the Runnable interface. But same problem occurs.

Upvotes: 0

Views: 563

Answers (1)

fabian
fabian

Reputation: 82461

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.

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.

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
  • Usually data access is not done from the UI layer. 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. If you do not want to use a ListView you should try reusing existing Labels as much as possible instead of replacing them with new instances...

Upvotes: 1

Related Questions