使用TextArea.append()时出现NullPointerException

时间:2014-08-12 20:51:10

标签: java multithreading nullpointerexception javafx append

我意识到标题中有一个几乎相同的问题。这个问题似乎与我的具体问题无关。

我正在使用JavaFX场景构建器来创建我的UI(包括所讨论的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);
    }
}

定义chatBox

的FXML行
<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)

1 个答案:

答案 0 :(得分:4)

注意:Passing Parameters 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. 关联
  6. 返回FXML对象层次结构的根对象
  7. 加载FXML后如何访问控制器

    要访问控制器,您必须创建FXMLLoader实例,而不是依赖(邪恶)静态FXMLLoader.load(URL)方法。您可以将URL FXML资源传递到FXMLLoader构造函数,也可以在setLocation(...)上调用FXMLLoader。然后,要加载FXML文件,只需在load()上调用无参数 FXMLLoader方法。完成后,您可以通过调用getController()实例上的FXMLLoader来访问控制器。

    您的代码中的其他问题

    您无法从后台线程更新UI:您必须从JavaFX应用程序线程更新它。就目前而言(如果您修复了NullPointerException问题),您的代码将在Java 8中抛出IllegalArgumentException。在JavaFX 2.2及更早版本中,您将不得不忍受在任意时间出现错误的可能性在将来。您可以通过将该代码包含在Platform.runLater()的调用中来安排在FX应用程序线程上执行的代码。

    不是很糟糕,但是设计不好,就是你在你的FXML控制器对之外暴露了UI元素(TextArea)。当你的老板进入你的办公室并且告诉你他不想再显示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);
        }
    }