【问题标题】:NullPointerException when using TextArea.append()使用 TextArea.append() 时出现 NullPointerException
【发布时间】:2014-08-12 20:51:10
【问题描述】:

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

我正在使用 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)

【问题讨论】:

  • 聊天框初始化了吗?
  • connectionconnection.reader 是否为空?
  • 什么时候开始发帖?顺便说一句:不要从 FX 应用程序线程以外的其他线程修改 UI!
  • 第28行是哪一个?
  • 1.您无法从后台线程更新 UI。可以想象这可能是问题的原因,但可能不是:无论如何您都需要修复它。 2.FXMLDocumentController.controller是什么?大概它是您的控制器类的一个实例,但它与FXMLLoader 创建的实例相同吗?您可能需要显示与此相关的代码。 3. 你确定你已经正确设置了fx:id 属性,以便TextArea 被注入到控制器中。显示相关的 FXML 代码。

标签: java multithreading nullpointerexception javafx append


【解决方案1】:

注意:Passing Parameters JavaFX FXML 确实完全涵盖了这一点。但是,您发布的代码远未正确构建以使用那里的方法,您可能需要专门针对您的示例查看它。不过,我强烈建议您通读该帖子并理解它。

为什么你会看到NullPointerException

FXMLDocumentController.controller 指的是FXMLDocumentController 的一个实例,它与FXMLLoader 为您创建的实例不同。因此,FXMLDocumentController.controller 中的 chatBox 字段没有被 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) 方法。您可以将 FXML 资源的 URL 传递给 FXMLLoader 构造函数,或者在 FXMLLoader 上调用 setLocation(...)。然后,要加载 FXML 文件,只需在 FXMLLoader 上调用 no-argument load() 方法。完成后,您可以通过在 FXMLLoader 实例上调用 getController() 来访问控制器。

代码中的其他问题

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

不是那么糟糕,但 imo 是一个糟糕的设计,因为您将 UI 元素(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);
    }
}

【讨论】:

  • 谢谢您,先生,您是一位绅士和一位学者!而且我一直在计划重构,我只是想先弄一个工作版本。
猜你喜欢
  • 2013-12-29
  • 2013-12-19
  • 2022-01-19
  • 2013-04-13
  • 2014-11-11
  • 2012-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多