NullPointerException при использовании TextArea.append ()

1

Я понимаю, что в названии есть почти одинаковый вопрос. Вопрос, похоже, не имеет отношения к моим конкретным вопросам.

Я использую построитель сценариев JavaFX для создания моего пользовательского интерфейса (включая TextArea). Все, что я хочу сделать, это взять сообщение, которое я получаю с сервера, и отправить его в текстовую область. Через различные заявления println я сузил проблему. Я пробовал различные решения (часами); Прибытие сюда было последним средством.

Единственная возможная причина, о которой я могу думать, - это что-то не так с многопоточным, но может даже начать думать о чем.

public class IncomingReader implements Runnable
{
    @Override
    public void run()
    {
        String message;

        try
        {
            while((message = Connection.connection.reader.readLine()) != null)
            {
                System.out.println("read" + message); //for debug, prints fine
                FXMLDocumentController.controller.chatBox
                        .appendText(message + "\n");  /////////PROBLEM HERE//////
            }
        }
        catch(IOException e)
        {
            System.out.println("Problem!"); // TODO: Catch better.
        }
    }
}

Класс контроллера FXML (только для соответствующей строки):

@FXML protected TextArea chatBox;

public class JavaChat extends Application 
{   
    @Override
    public void start(Stage stage) throws Exception {
        // Create and set up scene...
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
        Scene scene = new Scene(root);
        // Establish connection to server...
        Connection.createNewConnection();
        // Create new reader thread...
        Thread readerThread = new Thread(new IncomingReader());
        readerThread.start();
        // When all done...
        stage.setScene(scene);
        stage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
}

Линия FXML, которая определяет chatBox:

<TextArea id="ChatBox" fx:id="chatBox" focusTraversable="false" mouseTransparent="true" prefHeight="400.0" prefWidth="200.0" promptText="Chat:" wrapText="true" BorderPane.alignment="CENTER">

Исходящее исключение:

Exception in thread "Thread-4" java.lang.NullPointerException
    at javachat.IncomingReader.run(IncomingReader.java:28)
    at java.lang.Thread.run(Thread.java:745)
  • 0
    Окно чата было инициализировано?
  • 0
    Нулевое connection или connection.reader ?
Показать ещё 16 комментариев
Теги:
multithreading
javafx
nullpointerexception
append

1 ответ

4
Лучший ответ

Примечание. Это действительно полностью описано в разделе "Передача параметров JavaFX FXML". Тем не менее, код, который вы опубликовали, до сих пор не структурирован должным образом, чтобы использовать подходы там, что вам, вероятно, нужно увидеть его специально для вашего примера. Я бы настоятельно рекомендовал вам прочитать этот пост и понять его.

Почему вы видите NullPointerException :

FXMLDocumentController.controller ссылается на экземпляр FXMLDocumentController который не является тем же экземпляром, который был создан для вас FXMLLoader. Следовательно, поле chatBox в FXMLDocumentController.controller не было инициализировано FXMLLoader.

Что делает FXMLLoader :

Когда вы вызываете один из FXMLLoader.load(...), он в основном выполняет следующие действия:

  1. Разбирает файл FXML (или поток)
  2. Если корневой элемент FXML содержит атрибут fx:controller и никакой контроллер не установлен другими средствами, он создает экземпляр класса, указанного в этом атрибуте
  3. Создает иерархию объектов, описанную FXML. Если какой-либо из объектов, определенных в FXML, имеет атрибуты fx:id, а контроллер связан с FXMLLoader, он инициализирует аннотированные поля @FXML контроллера с соответствующими объектами
  4. Связывает обработчики событий с узлами, где определенные
  5. Возвращает корневой объект иерархии объектов FXML

Как получить доступ к контроллеру после загрузки FXML

Чтобы получить доступ к контроллеру, вы должны создать экземпляр FXMLLoader вместо того, чтобы полагаться на (злой) статический FXMLLoader.load(URL). Вы можете передать URL -адрес ресурса FXMLLoader конструктор FXMLLoader или вызвать setLocation(...) на FXMLLoader. Затем, чтобы загрузить файл FXML, просто вызовите метод load() без аргументов на FXMLLoader. Как только это будет завершено, вы сможете получить доступ к контроллеру, вызвав getController() в экземпляре FXMLLoader.

Другие проблемы в вашем коде

Вы не можете обновить пользовательский интерфейс из фонового потока: вы должны обновить его из приложения JavaFX Application Thread. Как бы то ни было (если вы исправите проблему NullPointerException), ваш код будет генерировать IllegalArgumentException в Java 8. В JavaFX 2.2 и ранее вам придется жить с возможностью появления ошибок в будущем. Вы можете запланировать код, который будет выполняться в потоке приложения FX, путем упаковки этого кода при вызове Platform.runLater().

Не совсем так, но плохой дизайн - это то, что вы подвергаете элементы пользовательского интерфейса (TextArea) вне пары FXML-контроллеров. Это становится реальной проблемой, когда ваш босс приходит в ваш офис и говорит, что он больше не хочет сообщений, отображаемых в TextArea, он хочет их в ListView. Поскольку TextArea разоблачен, вам нужно протащить весь свой код, ища любые ссылки на него. Лучший подход - определить appendMessage(String) в вашем контроллере и получить к нему доступ. Возможно, вы даже захотите включить все данные в отдельный класс модели и передать ссылки на экземпляр этого класса как на контроллер, так и на класс читателя, но это выходит за рамки этого вопроса.

Я воздержусь от жалобы на чрезмерное использование static материала..;).

Вот скелет одного из способов исправить этот код:

public class IncomingReader implements Runnable
{

    private final FXMLDocumentController controller ;

    public IncomingReader(FXMLDocumentController controller) {
        this.controller = controller ;
    }

    @Override
    public void run()
    {
        String message;

        try
        {
            while((message = Connection.connection.reader.readLine()) != null)
            {
                System.out.println("read" + message); //for debug, prints fine
                final String msg = message ;
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        controller.appendMessage(msg + "\n");  
                    }
                });
            }
        }
        catch(IOException e)
        {
            System.out.println("Problem!"); // TODO: Catch better.
        }
    }
}

контроллер:

public class FXMLDocumentController { 
    @FXML
    private TextArea chatBox ;

    public void appendMessage(String message) {
        chatBox.appendText(message);
    }

    // other stuff as before...
}

Заявка:

public class JavaChat extends Application 
{   
    @Override
    public void start(Stage stage) throws Exception {
        // Create and set up scene...
        FMXLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
        Parent root = loader.load();
        FXMLDocumentController controller = (FXMLDocumentController) loader.getController();
        Scene scene = new Scene(root);
        // Establish connection to server...
        Connection.createNewConnection();
        // Create new reader thread...
        Thread readerThread = new Thread(new IncomingReader(controller));
        readerThread.start();
        // When all done...
        stage.setScene(scene);
        stage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
}
  • 0
    Спасибо, сэр, вы джентльмен и ученый! И я всегда планировал провести рефакторинг, я просто пытался сначала получить рабочую версию.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню