我正在为自己的学习创建一个简单的聊天应用程序(桌面应用程序),并且我正在为我的客户端和服务器使用 netty 库。
我从线程启动客户端:new Thread(new Client()).start();
,我从我的Helper Class
执行此操作。
当客户端连接到服务器时,我想访问 MainController
并将其上的 Label
设置为 Connected
。
我正在使用 Guava Eventbus 来完成此任务。
我执行以下代码来实现它。
从我的 MainController 中,我订阅了将更改标签文本的函数:
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());
}
}
}
从我想发布客户端状态的客户端处理程序:
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**/
}
}
为了检查 EventBus 的此实现是否有效,我尝试从 Subscribe 函数中执行 println
并且它有效,
但是当我尝试 label_status.setText(status);
更改 Label
的 Text
时,出现 java.lang.NullPointerException
错误。
我不知道为什么,这是我第一次使用这两个库, 我阅读了 EventBus 的指南和示例,并根据我的理解我是如何做到这一点的。 我的代码有什么问题吗?我怎样才能达到我想要的目标?
注意:我在此应用程序中使用 JavaFX
。
更新:
我放弃使用Guava Eventbus,我用了greenrobot/EventBus与它的latest现在 jar 。
最佳答案
当加载和解析 FXML 文件时,
@FXML
注入(inject)的字段由 Controller 中的 FXMLLoader
初始化。您向事件总线注册的对象不是 Controller (它只是您创建的同一类的实例),因此 label_status
不会在向事件总线注册的对象中进行初始化。
您需要向事件总线注册实际 Controller ,并从客户端处理程序发布到该事件总线。您也不应该在客户端处理程序中引用 Controller (或其类):首先使用事件总线的全部目的是允许您解耦应用程序的这些部分。
所以你的客户端处理程序应该类似于
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**/
}
}
然后,在组装应用程序时,您将执行以下操作:
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...
将事件总线获取到客户端处理程序可能比上面的代码稍微复杂一些,但它应该为您提供基本概念。 (如果事情变得太复杂,您可以考虑使用依赖注入(inject)框架(例如 Spring 或 Guice)将事件总线注入(inject)到客户端处理程序中,并创建自动注册到事件总线的 Controller 。)
如果您愿意,您甚至可以更进一步,仅使用标准 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**/
}
}
然后
EventBus eventBus = new EventBus();
ClientHandler clientHandler = new ClientHandler(eventBus::post);
// etc ...
最后,请注意,由于您的客户端处理程序似乎正在后台线程上运行,因此您需要安排对 FX 应用程序线程上的标签进行更新:
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));
}
}
关于java - Guava 事件总线 : NullPointerException when Posting from Thread to UI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47508225/