@TopMetaFizick_010

Изменение формы из другого потока?

Разбираю чужой код, и не могу понять, как он работает.
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();

    }
}


Суть в том, что из конроллера вызывается метод start, тот в свою очередь создает новый поток вызывая приватный класс Receiver, из нового потока вызывается метод notifyObservers, который меняет значение у текстового поля в контроллере. Но как такое возвожно, если это новый поток? Делаю точно так-же, мне выскакивает ошибка:
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)


А код, который выше - работает, может дело в интерфейсах?
  • Вопрос задан
  • 502 просмотра
Пригласить эксперта
Ответы на вопрос 1
sergey-gornostaev
@sergey-gornostaev Куратор тега JavaFX
Седой и строгий
JavaFX, как и многие другие GUI-библиотеки, однопоточна. При создании окна создаётся Поток Обработки Событий, внутри которого будет работать цикл событий и обработчики событий. Вы не должны пытаться из главного потока или любого другого потока взаимодействовать с элементами графического интерфейса - это приведёт к сбою. Вы не должны внутри обработчиков событий запускать потоки - это приведёт к сбою. Вы не должны останавливать поток - это приведёт к сбою. Поэтому необходимо использовать предлагаемые библиотекой механизмы запуска фоновых задач и взаимодействия с ними - Platform.runLater(), Service, Task и т.п. Или можно придумывать собственные велосипеды, типа используемого в коде из вопроса, но тогда лучше сначала прочитать "Java Concurrency in Practice" Брайана Гетца и разобраться с паттернами.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы