简体   繁体   English

从线程发布到UI时,Guava Eventbus:NullPointerException

[英]Guava Eventbus: NullPointerException when Posting from Thread to UI

I'm creating a simple chat application(Desktop Application) for my own study and I'm using netty library for my Client and Server. 我正在为自己的研究创建一个简单的聊天应用程序(桌面应用程序),并且正在为客户端和服务器使用netty库。

I'm starting the client from Thread: new Thread(new Client()).start(); 我正在从线程启动客户端: new Thread(new Client()).start(); , I do this from my Helper Class . ,我从Helper Class做到这一点。 When the Client become Connected to the Server, I want to access the MainController and set the Label on it to Connected . 当客户端连接到服务器时,我要访问MainController并将其上的Label设置为Connected I'm using Guava Eventbus to accomplish this. 我正在使用Guava Eventbus完成此任务。

I do the following code to implement it. 我执行以下代码来实现它。

From my MainController where I subscribe the function that will change the Text of the Label: 在我的MainController中,我订阅了将更改Label文本的函数:

public class MainController implements Initializable{

    @FXML Label label_status;

    public MainController(){}

    @Override
    public void initialize(URL location, ResourceBundle resources) {
            /**Some Code Here...**/
    }

    /**Subscribe Eventbus function**/
    @Subscribe
    public void changeLabelStatus(String status) {
        try{
            label_status.setText(status);
        }catch (Exception e){
            System.out.println(TAG + "Failed to Change the status of Label. >> " + e.toString());
        }
    }
}

From the Handler of Client where I want to post the Status of the Client: 在我要发布客户端状态的客户端处理程序中:

public class ClientHandler extends SimpleChannelInboundHandler<Object>{

    EventBus eventBus;
    MainController mainController;

    public ClientHandler(){
        eventBus = new EventBus();
        mainController = new MainController();
        eventBus.register(mainController);
    }

    /**Change the Status when the Client become connected to Server**/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(TAG + "Successfully Connected to Server.);

        eventBus.post("Connected"); /**Post here**/
    }
}

To check if this implementation of EventBus will work, I tried to println from the Subscribe function and it works, but when I tried to label_status.setText(status); 为了检查EventBus的这种实现是否可行,我尝试从Subscribe函数中打印println ,并且可以正常工作,但是当我尝试label_status.setText(status); to change the Text of Label I get java.lang.NullPointerException error. 更改LabelText我得到java.lang.NullPointerException错误。

I have no idea why, this is my first time of using both library, I read the guide and example for EventBus and from my understanding this how I do it. 我不知道为什么,这是我第一次使用这两个库,我阅读了EventBus的指南和示例,并且据我所知,我该怎么做。 What's wrong with my code? 我的代码有什么问题? How can I achive what I want? 我怎样才能达到我想要的?

Note: I'm using JavaFX for this application. 注意:我正在为此应用程序使用JavaFX

UPDATE: 更新:

I give up using Guava Eventbus, I used greenrobot/EventBus with it's latest jar now. 我放弃使用Guava Eventbus,现在将greenrobot / EventBus最新的 jar一起使用。

@FXML -injected fields are initialized by the FXMLLoader in the controller when the FXML file is loaded and parsed. 加载和解析FXML文件时,@ FXML @FXML字段由控制器中的FXMLLoader初始化。 The object you are registering with the event bus is not the controller (it is just an instance of the same class that you created), so label_status will not be initialized in the object registered with the event bus. 您在事件总线中注册的对象不是控制器(它只是您创建的同一类的实例),因此label_status不会在事件总线中注册的对象中初始化。

You need to register the actual controller with the event bus, and post to that event bus from your client handler. 您需要向事件总线注册实际的控制器,然后从客户端处理程序发布到该事件总线。 You should also not have a reference to the controller (or its class) in the client handler: the whole point of using an event bus in the first place is to allow you to decouple these parts of the application. 您也不应在客户端处理程序中引用控制器(或其类):首先使用事件总线的全部目的是允许您分离应用程序的这些部分。

So your client handler should look something like 所以您的客户处理程序应该看起来像

public class ClientHandler extends SimpleChannelInboundHandler<Object>{

    private final EventBus eventBus;

    public ClientHandler(EventBus eventBus){
        this.eventBus = eventBus;
    }

    /**Change the Status when the Client become connected to Server**/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(TAG + "Successfully Connected to Server.);

        eventBus.post("Connected"); /**Post here**/
    }
}

Then at the point where you assemble your application you would do something along the following lines: 然后,在组装应用程序时,您将按照以下步骤进行操作:

EventBus eventBus = new EventBus();
ClientHandler clientHandler = new ClientHandler(eventBus);
// ...
FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml/file"));
Parent root = loader.load();
MainController controller = loader.getController();
eventBus.register(controller);
Scene scene = new Scene(root);
// put scene in stage and show stage, etc...

Getting the event bus to the client handler may be a little more complex than the code above, but it should give you the basic idea. 将事件总线传递到客户端处理程序可能比上面的代码复杂一些,但是应该可以带给您基本的思想。 (If things get too complicated here, you might consider using a dependency injection framework such as Spring or Guice to inject the event bus into the client handler, and create controllers which are automatically registered with the event bus.) (如果这里的事情变得太复杂了,您可以考虑使用诸如Spring或Guice之类的依赖项注入框架将事件总线注入客户端处理程序,并创建自动向事件总线注册的控制器。)

If you like, you can even go one step further and decouple the client handler from the event bus, just using standard Java API classes (the point here is that all that ClientHandler needs is "something that processes a String "): 如果愿意,您甚至可以更进一步,仅使用标准Java API类就可以将客户端处理程序与事件总线分离(此处的要点是, ClientHandler所需的全部是“处理String ”):

public class ClientHandler extends SimpleChannelInboundHandler<Object>{

    private final Consumer<String> statusUpdate ;

    public ClientHandler(Consumer<String> statusUpdate) {
        this.statusUpdate = statusUpdate ;
    }

    /**Change the Status when the Client become connected to Server**/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println(TAG + "Successfully Connected to Server.);

        statusUpdate.accept("Connected"); /**Post here**/
    }
}

and then 接着

EventBus eventBus = new EventBus();
ClientHandler clientHandler = new ClientHandler(eventBus::post);
// etc ...

Finally, note that since your client handler appears to be running on a background thread, you need to schedule the update to the label on the FX Application Thread: 最后,请注意,由于您的客户端处理程序似乎正在后台线程上运行,因此您需要安排对FX Application Thread上标签的更新:

public class MainController implements Initializable{

    @FXML Label label_status;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
            /**Some Code Here...**/
    }

    /**Subscribe Eventbus function**/
    @Subscribe
    public void changeLabelStatus(String status) {
        Platform.runLater(() -> label_status.setText(status));
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM