T
T
TopMetaFizick_0102019-03-03 03:00:30
JavaFX
TopMetaFizick_010, 2019-03-03 03:00:30

Reshaping from another thread?

I'm parsing someone else's code, and I can't figure out how it works.

public class ChatClient implements Notifier {
 private Set<MessageObserver> observers = new HashSet<>();
 public void start(String name, String password) {

        Receiver receiver = new Receiver();
        receiver.start();

    }

private class Receiver extends Thread {

        @Override
        public void run() {

            try {
 notifyObservers(new Message(-1000,"aa", "vv", "ss"));
 } catch (IOException e) {
                e.printStackTrace();
            }
}

public interface MessageObserver {

    void newPrivateMessageReceived(Message msg);
    void newBroadCastMessageReceived(Message msg);
    void sendMessageFailed(Message msg);
    void newLoginAnnouncement(Message msg);
    void newLogoutAnnouncement(Message msg);

             void loginSuccessMessage(Message msg);

    void loginFailedMessage(Message msg);
    void registerSuccessMessage(Message msg);
    void registerFailMessage(Message msg);

}

public interface Notifier {
    void addMessageObserver(MessageObserver o);
    void removeMessageObserver(MessageObserver o);
    void notifyObservers(Message m);
}

@Override
    public void notifyObservers(Message m) {
        if (m == null) {
            throw new NullPointerException("Message is null !");
        }
        for (MessageObserver observer : observers) {
                    observer.loginFailedMessage(m);
                   
}}

public class LoginController implements MessageObserver {

    private ChatClient model;

    @FXML
    private Stage stage;
    @FXML
    public Hyperlink registerHyperlink;
    @FXML
    TextField usernameField;
    @FXML
    PasswordField passwordField;
    @FXML
    Text resultMessage;
    @FXML
    BorderPane loginBorderPane;

    public void initialise(ChatClient model) {
        this.model = model;
    }

    @FXML
    public void handleLoginAction(ActionEvent actionEvent) {

        stage = (Stage) ((Node) actionEvent.getSource()).getScene().getWindow();
        String username = usernameField.getText();
        String password = passwordField.getText();

        //TODO validate username and pass

        model.start(username, password);

    }



 
    @Override
    public void registerFailMessage(Message msg) {
        resultMessage.setText(msg.getMessageBody());
    }
}

public class ChatApp extends Application {

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

    @Override
    public void start(Stage primaryStage) throws IOException {

        FXMLLoader loginLoader = new FXMLLoader(getClass().getResource("../resources/layouts/login_window.fxml"));
        Parent root = loginLoader.load();
        LoginController loginController = loginLoader.getController();
        //setUserAgentStylesheet(STYLESHEET_MODENA);

        ChatClient model = new ChatClient();
        loginController.initialise(model);
        model.addMessageObserver(loginController);

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setTitle("Login");
        primaryStage.setScene(scene);
        primaryStage.show();

    }
}

The bottom line is that the start method is called from the controller, which in turn creates a new thread by calling the private class Receiver, the notifyObservers method is called from the new thread, which changes the value of the text field in the controller. But how is it possible if this is a new stream? I do exactly the same, I get an error:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
  at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:279)
  at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
  at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
  at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
  at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
  at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
  at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
  at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
  at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(BehaviorSkinBase.java:197)
  at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
  at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
  at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
  at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
  at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
  at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
  at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)
  at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
  at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
  at javafx.scene.control.Labeled.setText(Labeled.java:145)
  at com.coursework.controllers.LoginWindowController.loadMainWindows(LoginWindowController.java:40)
  at com.coursework.logics.MainClient.d(MainClient.java:68)
  at com.coursework.logics.MainClient$connectionToServer.run(MainClient.java:49)

And the code that is above - works, maybe it's in the interfaces?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
Sergey Gornostaev, 2019-03-03
@sergey-gornostaev

JavaFX, like many other GUI libraries, is single-threaded. When a window is created , an Event Processing Thread is created , inside which the event loop and event handlers will work. You should not attempt to interact with GUI elements from the main thread or any other thread - this will crash. You should not start threads inside event handlers - this will lead to a crash. You should not stop the thread - it will crash. Therefore, it is necessary to use the mechanisms offered by the library for launching background tasks and interacting with them - Platform.runLater() , Service , Tasketc. Or you can come up with your own bicycles, such as used in the code from the question, but then it's better to read "Java Concurrency in Practice" by Brian Goetz first and understand the patterns .

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question