如何在 onMessage 事件侦听器中将项目添加到 ObservableList

How to add item to ObservableList in onMessage event listener

我正在使用 JavaFx 制作两个简单的 JMS 应用程序(一个用于发送方,另一个用于接收方)。

但是,我无法使用来自发件人的新到达消息刷新收件人的 GUI。

从调试中,我注意到接收器收到了来自服务器的消息,但是接收器无法将它添加到我的 ObservableList 中(当然,这导致 ListView 的 GUI 没有刷新)。

我在互联网上查找了 onMessage 事件,并覆盖了它(将一个项目添加到那里的 ObservableList),但它不起作用。引发事件后,没有向 ObservableList 添加任何元素。

这是我的接收器:

public class Administrator extends Application {
    private ObservableList<String> observableList;
    private final String DESTINATION_TYPE = "queue";
    private final String RECEIVE_CHANNEL = "askDestination";
    private final String SEND_CHANNEL = "answerDestination";
    private MessageConsumer messageConsumer;
    private MessageProducer messageProducer;
    private Session session;
    private Destination destination;
    private Connection connection;

    @FXML
    public TextField tfMessage;

    @FXML
    public ListView<String> lvMessage;

    @FXML
    public Button btnSend;

    @Override
    public void start(Stage stage) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("administratorUI.fxml"));
        stage.setTitle("Administrator");
        stage.setScene(new Scene(root, 640, 480));
        stage.setResizable(false);
        stage.show();
    }

    @Override
    public void init() throws Exception {
        super.init();
        lvMessage = new ListView<>();
        tfMessage = new TextField();
        //questionList = new ArrayList<>();
        observableList = FXCollections.observableArrayList();
        lvMessage.setItems(observableList);
        initService(RECEIVE_CHANNEL, DESTINATION_TYPE);
        getMessage(RECEIVE_CHANNEL);
        //Platform.runLater(this::updateLV);
    }

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

    public void onButtonAnswerClick() {
        String message = tfMessage.getText();

        if (message.equals("")) {
            Alert alert = new Alert(Alert.AlertType.ERROR);
            alert.setContentText("Please enter your message!!");
            alert.show();
            return;
        }

        if (replyToQuestion(message, SEND_CHANNEL)) {
            tfMessage.clear();
        } else {
            handleServiceError("Service Error", "Could not send the message to the service");
        }
    }

    private void handleServiceError(String errorTitle, String errorText){
        Alert error = new Alert(Alert.AlertType.ERROR);
        error.setTitle(errorTitle);
        error.setContentText(errorText);
    }

    private void updateLV(){
        lvMessage.getItems().clear();
        lvMessage.setItems(observableList);
    }

    private void initService(String targetDestination, String destinationType){
        try {
            Properties props = new Properties();
            props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
                    "org.apache.activemq.jndi.ActiveMQInitialContextFactory");
            props.setProperty(Context.PROVIDER_URL, "tcp://localhost:61616");

            // connect to the Destination called “myFirstChannel”
            // queue or topic: “queue.myFirstDestination” or “topic.myFirstDestination”
            props.put((destinationType + "." + targetDestination), targetDestination);
            Context jndiContext = new InitialContext(props);
            ConnectionFactory connectionFactory = (ConnectionFactory) jndiContext.lookup("ConnectionFactory");

            // to connect to the JMS
            connection = connectionFactory.createConnection();

            // session for creating consumers
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            // connect to the receiver destination
            //reference to a queue/topic destination
            destination = (Destination) jndiContext.lookup(targetDestination);
        } catch (NamingException | JMSException e) {
            e.printStackTrace();
        }
    }

    private boolean replyToQuestion(String message, String sendDestination) {
        try {
            initService(sendDestination, DESTINATION_TYPE);

            // for sending messages
            messageProducer = session.createProducer(destination);

            // create a text message
            Message msg = session.createTextMessage(message);

            msg.setJMSMessageID("222");
            System.out.println(msg.getJMSMessageID());

            // send the message
            messageProducer.send(msg);

            //questionList.add(message);
            updateLV();
            return true;
        } catch (JMSException e) {
            e.printStackTrace();
            return false;
        }
    }

    private void getMessage(String receiveDestination) {
        try {
            initService(receiveDestination, DESTINATION_TYPE);

            // this is needed to start receiving messages
            connection.start();

            // for receiving messages
            messageConsumer = session.createConsumer(destination);
            MessageListener listener = message -> {
                try {
                    observableList.add(((TextMessage)message).getText());
                } catch (JMSException e) {
                    e.printStackTrace();
                }
            };

            messageConsumer.setMessageListener(listener);
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

我的发件人:

    public class User extends Application implements MessageListener {
    private static List<String> questions;
    private final String DESTINATION_TYPE = "queue";
    private final String RECEIVE_CHANNEL = "answerDestination";
    private final String SEND_CHANNEL = "askDestination";
    private String requestId;
    private MessageConsumer messageConsumer;
    private MessageProducer messageProducer;
    private Session session;
    private Destination destination;
    private Connection connection;

    @FXML
    public Button btnSend;

    @FXML
    public TextField tfMessage;

    @FXML
    public ListView lvMessage;

    @Override
    public void start(Stage stage) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("userUI.fxml"));
        stage.setTitle("Sender");
        stage.setScene(new Scene(root, 640, 480));
        stage.setResizable(false);
        stage.show();
    }

    @Override
    public void init() throws Exception {
        super.init();
        btnSend = new Button();
        tfMessage = new TextField();
        lvMessage = new ListView();
        questions = new ArrayList<>();
        initService(RECEIVE_CHANNEL, DESTINATION_TYPE);
        getAnswer(RECEIVE_CHANNEL);
    }

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

    public void onButtonSendClick(javafx.event.ActionEvent actionEvent) {
        String message = tfMessage.getText();

        if (message.equals("")) {
            Alert alert = new Alert(Alert.AlertType.ERROR);
            alert.setContentText("Please enter your question!!");
            alert.show();
            return;
        }

        if (sendQuestion(message, SEND_CHANNEL)) {
            tfMessage.clear();
        } else {
            handleServiceError("Service Error", "Could not send the message tot the service");
        }
    }

    private void handleServiceError(String errorTitle, String errorText){
        Alert error = new Alert(Alert.AlertType.ERROR);
        error.setTitle(errorTitle);
        error.setContentText(errorText);
    }

    private void updateLV(){
        lvMessage.getItems().clear();
        lvMessage.getItems().addAll(questions);
    }

    private void initService(String targetDestination, String destinationType){
        try {
            Properties props = new Properties();
            props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
                    "org.apache.activemq.jndi.ActiveMQInitialContextFactory");
            props.setProperty(Context.PROVIDER_URL, "tcp://localhost:61616");

            // connect to the Destination called “myFirstChannel”
            // queue or topic: “queue.myFirstDestination” or “topic.myFirstDestination”
            props.put((destinationType + "." + targetDestination), targetDestination);
            Context jndiContext = new InitialContext(props);
            ConnectionFactory connectionFactory = (ConnectionFactory) jndiContext.lookup("ConnectionFactory");

            // to connect to the JMS
            connection = connectionFactory.createConnection();
            // session for creating consumers
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            // connect to the receiver destination
            //reference to a queue/topic destination
            destination = (Destination) jndiContext.lookup(targetDestination);
        } catch (NamingException | JMSException e) {
            e.printStackTrace();
        }
    }

    private boolean sendQuestion(String message, String sendDestination) {
        try {
            initService(sendDestination, DESTINATION_TYPE);

            // for sending messages
            messageProducer = session.createProducer(destination);

            // create a text message
            Message msg = session.createTextMessage(message);

            msg.setJMSMessageID("111");
            System.out.println(msg.getJMSMessageID());

            // send the message
            messageProducer.send(msg);

            //questionList.add(message);
            updateLV();
            return true;
        } catch (JMSException e) {
            e.printStackTrace();
            return false;
        }
    }

    private void getAnswer(String receiveDestination) {
        try {
            initService(receiveDestination, DESTINATION_TYPE);

            // for receiving messages
            messageConsumer = session.createConsumer(destination);
            messageConsumer.setMessageListener(this);


            // this is needed to start receiving messages
            connection.start();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onMessage(Message message) {
        try {
            questions.add(((TextMessage)message).getText());
            requestId = message.getJMSMessageID();
            System.out.println(requestId);
            updateLV();
        } catch (JMSException e) {
            e.printStackTrace();
        }
    }
}

接收方的 FXML:

    <GridPane hgap="10" prefHeight="400.0" prefWidth="600.0" vgap="10" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Administrator">
    <padding>
        <Insets bottom="10" left="10" right="10" top="10" />
    </padding>
    <Label text="Current question:" textFill="cornflowerblue" GridPane.columnIndex="0" GridPane.rowIndex="0">
        <font>
            <Font name="System Bold" size="15.0" />
        </font>
    </Label>
    <ListView fx:id="lvMessage" prefWidth="600" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="1">

    </ListView>

    <HBox spacing="10" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.rowIndex="2">
        <TextField fx:id="tfMessage" prefHeight="61.0" prefWidth="504.0" promptText="Type your answer here...">
            <font>
                <Font size="15.0" />
            </font>
        </TextField>
        <Button fx:id="btnSend" onAction="#onButtonAnswerClick" prefHeight="61.0" prefWidth="88.0" text="Send">
        </Button>
    </HBox>
   <columnConstraints>
      <ColumnConstraints />
      <ColumnConstraints />
   </columnConstraints>
   <rowConstraints>
      <RowConstraints />
      <RowConstraints />
      <RowConstraints />
   </rowConstraints>
</GridPane>

接收者收到消息后,事件被引发,所以我希望 ListView 被新消息更新。但是,没有元素被添加到 ObservableList => ListView 中没有更新。

所以,请问我的做法是错误的还是正确的?

原来我在错误的线程上更新了 ListView。对于那些仍然对答案感兴趣的人,我使用了: Platform.runLater() 并在 start 方法中注册了事件侦听器(在 stage.show() 之前)