"Not on FX application thread" Netty 和 JavaFX 异常
"Not on FX application thread" exception with Netty and JavaFX
我正在创建一个 Netty/JavaFX 应用程序,当我尝试从客户端向服务器发送图片时遇到以下异常。
Exception in thread "nioEventLoopGroup-3-1" java.lang.IllegalStateException: Not on FX application thread; currentThread = nioEventLoopGroup-3-1
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:364)
at javafx.scene.Scene.addToDirtyList(Scene.java:485)
at javafx.scene.Node.addToSceneDirtyList(Node.java:424)
at javafx.scene.Node.impl_markDirty(Node.java:415)
at javafx.scene.Node.notifyParentsOfInvalidatedCSS(Node.java:8709)
at javafx.scene.Node.requestCssStateTransition(Node.java:8639)
at javafx.scene.Node.pseudoClassStateChanged(Node.java:8680)
at javafx.scene.Node$FocusedProperty.markInvalid(Node.java:7535)
at javafx.scene.Node$FocusedProperty.store(Node.java:7520)
at javafx.scene.Node.setFocused(Node.java:7578)
at javafx.scene.control.TableRow.updateFocus(TableRow.java:305)
at javafx.scene.control.TableRow.lambda$new(TableRow.java:106)
at javafx.scene.control.TableRow$$Lambda7/862831050.invalidated(Unknown Source)
at javafx.beans.WeakInvalidationListener.invalidated(WeakInvalidationListener.java:83)
at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:349)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.property.ReadOnlyObjectWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyObjectWrapper.java:176)
at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:142)
at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:145)
at javafx.scene.control.TableView$TableViewFocusModel.setFocusedCell(TableView.java:2981)
at javafx.scene.control.TableView$TableViewFocusModel.focus(TableView.java:3022)
at javafx.scene.control.TableView$TableViewSelectionModel.focus(TableView.java:1941)
at javafx.scene.control.TableView$TableViewSelectionModel.focus(TableView.java:1935)
at javafx.scene.control.TableView$TableViewSelectionModel.focus(TableView.java:1918)
at javafx.scene.control.TableView$TableViewArrayListSelectionModel.clearSelection(TableView.java:2554)
at javafx.scene.control.TableView$TableViewArrayListSelectionModel.onChanged(TableView.java:2036)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:183)
at network.ServerHandler.handlerRemoved(ServerHandler.java:53)
at io.netty.channel.DefaultChannelPipeline.callHandlerRemoved0(DefaultChannelPipeline.java:527)
at io.netty.channel.DefaultChannelPipeline.callHandlerRemoved(DefaultChannelPipeline.java:521)
at io.netty.channel.DefaultChannelPipeline.remove0(DefaultChannelPipeline.java:351)
at io.netty.channel.DefaultChannelPipeline.destroyDown(DefaultChannelPipeline.java:798)
at io.netty.channel.DefaultChannelPipeline.destroyUp(DefaultChannelPipeline.java:767)
at io.netty.channel.DefaultChannelPipeline.destroy(DefaultChannelPipeline.java:759)
at io.netty.channel.DefaultChannelPipeline.fireChannelUnregistered(DefaultChannelPipeline.java:743)
at io.netty.channel.AbstractChannel$AbstractUnsafe.run(AbstractChannel.java:615)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:380)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357)
at io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:116)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:745)
当我将我的应用程序作为 javafx 应用程序启动时抛出此异常。当我将其作为控制台应用程序启动时,客户端与服务器断开连接但未显示任何错误或异常。
客户处理程序:
class ClientHandler extends ChannelInboundMessageHandlerAdapter<Object> {
@Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
MessageWrapper message = (MessageWrapper) msg;
System.out.println(message);
switch (message.getHeader()) {
case "IMG":
takeAndSendScreenShot();
default:;
}
}
private void takeAndSendScreenShot() throws IOException {
// try {
// //originalImage = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
//
// } catch (AWTException ex) {
// Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
// }
BufferedImage originalImage = ImageIO.read(new File("c:\test.jpg"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(originalImage, "jpg", baos);
baos.flush();
byte[] imageInByte = baos.toByteArray();
baos.close();
Client.getChannel().write(new MessageWrapper("IMG", baos));
// Client.getChannel().write(new MessageWrapper("TEST", "TEST"));
}
}
服务器:
public class 服务器 {
private static final Logger log = Logger.getLogger(Server.class.getName());
private final int port;
public Server(int port) {
this.port = port;
}
public void run() throws InterruptedException {
EventLoopGroup mainGroup = new NioEventLoopGroup();
EventLoopGroup slaveGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstarp = new ServerBootstrap()
.group(mainGroup, slaveGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerInitaizer());
bootstarp.bind(port).sync().channel().closeFuture().sync();
} finally {
mainGroup.shutdownGracefully();
slaveGroup.shutdownGracefully();
}
}
}
服务器初始化:
public class ServerInitaizer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
// pipeline.addLast("decoder", new ObjectDecoder(ClassResolvers.softCachingResolver(ClassLoader.getSystemClassLoader())));
pipeline.addLast("decoder", new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(ClassLoader.getSystemClassLoader())));
pipeline.addLast("encoder", new ObjectEncoder());
pipeline.addLast("handler", new ServerHandler());
}
}
服务器处理程序:
public class ServerHandler extends SimpleChannelInboundHandler<Object> {
private static final Logger log = Logger.getLogger(Server.class.getName());
private static final ChannelGroup channels = new DefaultChannelGroup(
GlobalEventExecutor.INSTANCE);
private static final ObservableList<Client> clients
= FXCollections.observableArrayList(
(Client c) -> new Observable[]{c.userNameProperty(), c.remoteAddressProperty()});
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
log.info(ctx.toString());
Channel incoming = ctx.channel();
channels.add(incoming);
clients.add(new Client(incoming.remoteAddress().toString(), ""));
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel incoming = null;
log.info(ctx.toString());
try {
incoming = ctx.channel();
for (int i = clients.size() - 1; i > -1; i--) {
if (incoming.remoteAddress().toString().equals(clients.get(i).getRemoteAddress())) {
clients.remove(i);
}
}
} finally {
channels.remove(incoming);
}
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg)
throws Exception {
log.info(msg.toString());
Channel incoming = ctx.channel();
MessageWrapper message = (MessageWrapper) msg;
switch (message.getHeader()) {
case "MSG":
saveMessage(incoming, message.getContent().toString());
case "USER":
setUser(incoming, message.getContent().toString());
default:;
}
}
消息包装器:
@Data
public class MessageWrapper implements Serializable{
private static final long serialVersionUID = 1L;
String header;
Object content;
public MessageWrapper(String message, Object content) {
this.header = message;
this.content = content;
}
}
Main.java:
public class Main extends Application {
public static void main(String[] args) throws InterruptedException {
launch(args);
//new Server(8000).run();
}
@Override
public void start(Stage stage) throws Exception {
Task<Integer> task = new Task<Integer>() {
@Override
protected Integer call() {
try {
new Server(8000).run();
} catch (Exception e) {
System.out.println(e);
}
return 1;
}
};
Thread th = new Thread(task);
th.setDaemon(true);
System.out.println("Starting server task...");
th.start();
Parent root = FXMLLoader.load(getClass().getResource("/fxml/TablePanel.fxml"));
Scene scene = new Scene(root);
stage.setTitle("FXML Welcome");
stage.setScene(scene);
stage.show();
}
}
如果有人能告诉我问题出在哪里,或者我怎样才能获得有关此问题的更多信息。我真的很感激。
您有一个用作 JavaFX 控件模型的 ObservableList,并且您正在从 Netty 线程更新该列表。这会导致 JavaFX 尝试从 Netty 线程更新 UI,这是不允许的(所有 UI 操作都需要在应用程序线程中执行)。您需要使用 Platform.runLater()
将执行转移到应用程序线程。
我正在创建一个 Netty/JavaFX 应用程序,当我尝试从客户端向服务器发送图片时遇到以下异常。
Exception in thread "nioEventLoopGroup-3-1" java.lang.IllegalStateException: Not on FX application thread; currentThread = nioEventLoopGroup-3-1
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:364)
at javafx.scene.Scene.addToDirtyList(Scene.java:485)
at javafx.scene.Node.addToSceneDirtyList(Node.java:424)
at javafx.scene.Node.impl_markDirty(Node.java:415)
at javafx.scene.Node.notifyParentsOfInvalidatedCSS(Node.java:8709)
at javafx.scene.Node.requestCssStateTransition(Node.java:8639)
at javafx.scene.Node.pseudoClassStateChanged(Node.java:8680)
at javafx.scene.Node$FocusedProperty.markInvalid(Node.java:7535)
at javafx.scene.Node$FocusedProperty.store(Node.java:7520)
at javafx.scene.Node.setFocused(Node.java:7578)
at javafx.scene.control.TableRow.updateFocus(TableRow.java:305)
at javafx.scene.control.TableRow.lambda$new(TableRow.java:106)
at javafx.scene.control.TableRow$$Lambda7/862831050.invalidated(Unknown Source)
at javafx.beans.WeakInvalidationListener.invalidated(WeakInvalidationListener.java:83)
at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:349)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.property.ReadOnlyObjectWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyObjectWrapper.java:176)
at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:142)
at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:145)
at javafx.scene.control.TableView$TableViewFocusModel.setFocusedCell(TableView.java:2981)
at javafx.scene.control.TableView$TableViewFocusModel.focus(TableView.java:3022)
at javafx.scene.control.TableView$TableViewSelectionModel.focus(TableView.java:1941)
at javafx.scene.control.TableView$TableViewSelectionModel.focus(TableView.java:1935)
at javafx.scene.control.TableView$TableViewSelectionModel.focus(TableView.java:1918)
at javafx.scene.control.TableView$TableViewArrayListSelectionModel.clearSelection(TableView.java:2554)
at javafx.scene.control.TableView$TableViewArrayListSelectionModel.onChanged(TableView.java:2036)
at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:183)
at network.ServerHandler.handlerRemoved(ServerHandler.java:53)
at io.netty.channel.DefaultChannelPipeline.callHandlerRemoved0(DefaultChannelPipeline.java:527)
at io.netty.channel.DefaultChannelPipeline.callHandlerRemoved(DefaultChannelPipeline.java:521)
at io.netty.channel.DefaultChannelPipeline.remove0(DefaultChannelPipeline.java:351)
at io.netty.channel.DefaultChannelPipeline.destroyDown(DefaultChannelPipeline.java:798)
at io.netty.channel.DefaultChannelPipeline.destroyUp(DefaultChannelPipeline.java:767)
at io.netty.channel.DefaultChannelPipeline.destroy(DefaultChannelPipeline.java:759)
at io.netty.channel.DefaultChannelPipeline.fireChannelUnregistered(DefaultChannelPipeline.java:743)
at io.netty.channel.AbstractChannel$AbstractUnsafe.run(AbstractChannel.java:615)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:380)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:357)
at io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:116)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:745)
当我将我的应用程序作为 javafx 应用程序启动时抛出此异常。当我将其作为控制台应用程序启动时,客户端与服务器断开连接但未显示任何错误或异常。
客户处理程序:
class ClientHandler extends ChannelInboundMessageHandlerAdapter<Object> {
@Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
MessageWrapper message = (MessageWrapper) msg;
System.out.println(message);
switch (message.getHeader()) {
case "IMG":
takeAndSendScreenShot();
default:;
}
}
private void takeAndSendScreenShot() throws IOException {
// try {
// //originalImage = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
//
// } catch (AWTException ex) {
// Logger.getLogger(ClientHandler.class.getName()).log(Level.SEVERE, null, ex);
// }
BufferedImage originalImage = ImageIO.read(new File("c:\test.jpg"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(originalImage, "jpg", baos);
baos.flush();
byte[] imageInByte = baos.toByteArray();
baos.close();
Client.getChannel().write(new MessageWrapper("IMG", baos));
// Client.getChannel().write(new MessageWrapper("TEST", "TEST"));
}
}
服务器: public class 服务器 {
private static final Logger log = Logger.getLogger(Server.class.getName());
private final int port;
public Server(int port) {
this.port = port;
}
public void run() throws InterruptedException {
EventLoopGroup mainGroup = new NioEventLoopGroup();
EventLoopGroup slaveGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstarp = new ServerBootstrap()
.group(mainGroup, slaveGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ServerInitaizer());
bootstarp.bind(port).sync().channel().closeFuture().sync();
} finally {
mainGroup.shutdownGracefully();
slaveGroup.shutdownGracefully();
}
}
} 服务器初始化:
public class ServerInitaizer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
// pipeline.addLast("decoder", new ObjectDecoder(ClassResolvers.softCachingResolver(ClassLoader.getSystemClassLoader())));
pipeline.addLast("decoder", new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(ClassLoader.getSystemClassLoader())));
pipeline.addLast("encoder", new ObjectEncoder());
pipeline.addLast("handler", new ServerHandler());
}
}
服务器处理程序:
public class ServerHandler extends SimpleChannelInboundHandler<Object> {
private static final Logger log = Logger.getLogger(Server.class.getName());
private static final ChannelGroup channels = new DefaultChannelGroup(
GlobalEventExecutor.INSTANCE);
private static final ObservableList<Client> clients
= FXCollections.observableArrayList(
(Client c) -> new Observable[]{c.userNameProperty(), c.remoteAddressProperty()});
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
log.info(ctx.toString());
Channel incoming = ctx.channel();
channels.add(incoming);
clients.add(new Client(incoming.remoteAddress().toString(), ""));
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel incoming = null;
log.info(ctx.toString());
try {
incoming = ctx.channel();
for (int i = clients.size() - 1; i > -1; i--) {
if (incoming.remoteAddress().toString().equals(clients.get(i).getRemoteAddress())) {
clients.remove(i);
}
}
} finally {
channels.remove(incoming);
}
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg)
throws Exception {
log.info(msg.toString());
Channel incoming = ctx.channel();
MessageWrapper message = (MessageWrapper) msg;
switch (message.getHeader()) {
case "MSG":
saveMessage(incoming, message.getContent().toString());
case "USER":
setUser(incoming, message.getContent().toString());
default:;
}
}
消息包装器:
@Data
public class MessageWrapper implements Serializable{
private static final long serialVersionUID = 1L;
String header;
Object content;
public MessageWrapper(String message, Object content) {
this.header = message;
this.content = content;
}
}
Main.java:
public class Main extends Application {
public static void main(String[] args) throws InterruptedException {
launch(args);
//new Server(8000).run();
}
@Override
public void start(Stage stage) throws Exception {
Task<Integer> task = new Task<Integer>() {
@Override
protected Integer call() {
try {
new Server(8000).run();
} catch (Exception e) {
System.out.println(e);
}
return 1;
}
};
Thread th = new Thread(task);
th.setDaemon(true);
System.out.println("Starting server task...");
th.start();
Parent root = FXMLLoader.load(getClass().getResource("/fxml/TablePanel.fxml"));
Scene scene = new Scene(root);
stage.setTitle("FXML Welcome");
stage.setScene(scene);
stage.show();
}
}
如果有人能告诉我问题出在哪里,或者我怎样才能获得有关此问题的更多信息。我真的很感激。
您有一个用作 JavaFX 控件模型的 ObservableList,并且您正在从 Netty 线程更新该列表。这会导致 JavaFX 尝试从 Netty 线程更新 UI,这是不允许的(所有 UI 操作都需要在应用程序线程中执行)。您需要使用 Platform.runLater()
将执行转移到应用程序线程。