В моем приложении JavaFX у меня есть метка, которую я хочу обновить с помощью StringProperty другого класса, который определяет функциональность сервера.
В классе сервера у меня есть StringProperty, как показано ниже:
public class eol_server {
private StringProperty serverMessagesProperty;
etc..
У меня есть способ вернуть StringProperty по запросу:
public StringProperty serverMessagesProperty() {
if (serverMessagesProperty == null) {
serverMessagesProperty = new SimpleStringProperty();
}
return serverMessagesProperty;
}
В основном классе графического интерфейса, метод Start (), я создаю сцену, а затем создаю экземпляр нового объекта сервера. После этого я обновляю одну из меток в моем графике сцены, привязывая ее к StringProperty объекта сервера:
public void start(Stage primaryStage) {
...set up all the gui components
primaryStage.show();
dss = new eol_server();
lbl_dssMessage.textProperty().bind(dss.getServerMessagesProperty());
}
Когда я запускаю приложение, сцена отображается так, как я ожидал, и для текста lbl_dssMessage устанавливается значение, установленное в конструкторе eol_server. Но с этого момента привязка не работает, хотя у меня есть действия, которые обновляют StringProperty объекта dss, они не обновляют метку в графическом интерфейсе.
Вот полный файл, который генерирует урезанную версию сцены:
public class eol_gui extends Application {
private static eol_server dss = null;
private static Stage primaryStage;
/**
* Application Main Function
* @param args
*/
public static void main(String[] args) {
launch(args);
}
/**
* @param stage
*/
private void setPrimaryStage(Stage stage) {
eol_gui.primaryStage = stage;
}
/**
* @return Stage for GUI
*/
static public Stage getPrimaryStage() {
return eol_gui.primaryStage;
}
/* (non-Javadoc)
* @see javafx.application.Application#start(javafx.stage.Stage)
*/
@Override
public void start(Stage primaryStage) {
setPrimaryStage(primaryStage);
BorderPane root = new BorderPane();
Label lbl_dssMessage = new Label("initialized");
HBox topMenu = new HBox();
topMenu.getChildren().add(lbl_dssMessage);
eol_supervisor_I config1 = new eol_supervisor_I();
// Build Scene
root.setStyle("-fx-background-color: #FFFFFF;");
root.setTop(topMenu);
primaryStage.setScene(new Scene(root, 300, 50));
primaryStage.show();
dss = new eol_server();
dss.getServerMessagesProperty().addListener(new ChangeListener<Object>() {
@Override
public void changed(ObservableValue<?> o,Object oldVal,Object newVal)
{
//Option 1: lbl_dssMessage.setText(newVal.toString());
}
});
//Option 2
lbl_dssMessage.textProperty().bind(dss.serverMessagesProperty);
}
}
Как видите, я пробовал метод привязки и прослушиватель изменений. Похоже, что привязка работала, как и прослушиватель, во всех случаях, кроме тех, которые выполняются в потоках службы сервера. Они вызывают исключение IllegalStateException из-за отсутствия в основном потоке приложения JavaFX. Как безопасно и правильно обмениваться сообщениями из сервиса в основную ветку?
Сервер определен в следующем классе, который предназначен для запуска служб независимо от основного потока JavaFX. Но я хотел бы обмениваться информацией между потоками, чтобы показать статус. Я пытаюсь избежать зависания графического интерфейса пользователя во время подключения к серверу и обмена данными.
public class eol_server {
public StringProperty serverMessagesProperty;
public eol_server() {
/* Create Scripting Environment */
serverMessagesProperty().set("Establishing Debug Server Environment");
connect();
}
public boolean connect() {
serverMessagesProperty().set("Creating Server");
ConnectService connectService = new ConnectService();
connectService.start();
return false;
}
public StringProperty serverMessagesProperty() {
if (serverMessagesProperty == null) {
serverMessagesProperty = new SimpleStringProperty();
}
return serverMessagesProperty;
}
public StringProperty getServerMessagesProperty() {
return serverMessagesProperty;
}
private class ConnectService extends Service<Void> {
@Override
protected void succeeded() {
}
@Override
protected void failed() {
}
@Override
protected void cancelled() {
}
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
serverMessagesProperty().set("Connecting....");
Thread.sleep(5000);
// DEMO: uncomment to provoke "Not on FX application thread"-Exception:
// connectButton.setVisible(false);
serverMessagesProperty().set("Waiting for server feedback");
Thread.sleep(5000);
return null;
}
};
}
}
private class DisconnectService extends Service<Void> {
@Override
protected void succeeded() {
}
@Override
protected void cancelled() {
}
@Override
protected Task<Void> createTask() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
updateMessage("Disconnecting....");
serverMessagesProperty().set("Disconnecting....");
Thread.sleep(5000);
updateMessage("Waiting for server feedback.");
serverMessagesProperty().set("Waiting for server feedback.");
Thread.sleep(5000);
return null;
}
};
}
}
}
заранее спасибо
JW
думал, что это не решит проблему - посмотрите Соглашения кода Java для именования классов





Кажется, у меня есть решение, основанное на том, что я прочитал о методе платформы runlater (). Также см. Обсуждение здесь
Ошибка многопоточности при привязке StringProperty
изменение моего слушателя для вызова обновления сообщения через runLater, похоже, ломает проблему многопоточности. Да, я ожидаю, что это произойдет не сразу, но будет очень близко к немедленному и достаточно хорошим. Я понимаю, что JavaFX требует, чтобы вы не связывались со значениями / узлами потоков приложения и т.д. и т.д. графа сцены, но это довольно сложная область библиотеки JavaFX.
Вот слушатель, который работал у меня
// Get new DSS session active
dss = new eol_server();
dss.serverMessagesProperty.addListener(new ChangeListener<Object>() {
@Override
public void changed (ObservableValue<?> observable, Object oldValue, Object newValue) {
Platform.runLater(() -> lbl_dssMessage.setText(newValue.toString()));
}
});
}
Рад за дальнейшее обсуждение того, почему это работает, когда другие варианты не работают, также открыты для любых других более элегантных предложений.
Прочтите приведенный ниже абзац из документации Узел JavaFX.
Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene in a Window that is showing. An application must attach nodes to such a Scene or modify them on the JavaFX Application Thread.
Правильный и более функциональный способ решения вашей проблемы -
dss.serverMessagesProperty.addListener((observable, oldValue, newValue) -> Platform.runLater(() -> lbl_dssMessage.setText(newValue));
Нет, привязка не должна теряться. Если вы обновляете другой экземпляр
eol_serverили обновляетеserverMessagesPropertyиз потока, отличного от потока приложения javafx, это может вызвать проблемы. Блокировка потока приложения JavaFX с помощью длительной операции также может привести к такой проблеме. Невозможно сказать без минимальный воспроизводимый пример.