使用JFXPanel Swing interop防止JavaFX线程死亡?
我将几个使用JFXPanel Swing interop防止JavaFX线程死亡?,java,swing,javafx,interop,javafx-8,Java,Swing,Javafx,Interop,Javafx 8,我将几个JFXPanels嵌入到Swing应用程序中,当JFXPanels不再可见时,JavaFX线程将消失。这是有问题的,因为在JavaFX线程死亡后创建另一个JFXPanel不会启动另一个JavaFX线程,因此JFXPanel将为空 据我所知,JFXPanel ctor通过调用以下命令启动JavaFX线程: PlatformImpl.startup(new Runnable() { @Override public void run() { // No need to do
JFXPanels
嵌入到Swing应用程序中,当JFXPanels不再可见时,JavaFX线程将消失。这是有问题的,因为在JavaFX线程死亡后创建另一个JFXPanel
不会启动另一个JavaFX线程,因此JFXPanel
将为空
据我所知,JFXPanel ctor通过调用以下命令启动JavaFX线程:
PlatformImpl.startup(new Runnable() {
@Override public void run() {
// No need to do anything here
}
});
稍后,一旦JFXPanel
具有父组件,就会调用其addNotify
方法,该方法调用registerFinishListener
,该方法使用PlatformImpl
注册PlatformImpl.FinishListener()
。当调用PlatformImpl.checkIdle()
时,注册FinishListener
的行为可以防止JavaFX线程死亡
当一个JFXPanel
不再可见时,它的removeNotify
方法是调用deregisterFinishListener()
:
当instanceCount
为零时,FinishListener
将被删除,这将导致platformpl
在以下代码中调用platformpl.tkExit
,从而导致JavaFX线程死亡:
private static void notifyFinishListeners(boolean exitCalled) {
// Notify listeners if any are registered, else exit directly
if (listenersRegistered.get()) {
for (FinishListener l : finishListeners) {
if (exitCalled) {
l.exitCalled();
} else {
l.idle(implicitExit);
}
}
} else if (implicitExit || platformExit.get()) {
tkExit();
}
}
我发现解决这个问题的唯一方法是在Swing应用程序开始时调用Platform.setImplicitExit(false)
,这样JavaFX线程就不会自动消亡。此修复需要在应用程序退出时调用Platform.exit()
,否则JavaFX线程将阻止进程停止
这似乎是JavaFX Swing interop中的一个bug,或者至少应该修改interop文档,通过讨论Platform.setImplicitExit(false)
来解决这个问题
另一个解决方案是允许在创建另一个JFXPanel但被PlatformImpl.startup(Runnable)
阻止时创建新的JavaFX线程:
我遗漏了什么吗?这是一个非常古老的“bug”,随着Platform.setImplicitExit(false)
的引入,这个bug得到了一定程度的修复。您可以阅读开放版中的开发者评论。正如您将看到的,它的优先级较低,可能永远不会得到修复(至少不会很快)
您可能希望尝试的另一种解决方案是扩展当前主类中的应用程序类,并使用主阶段显示应用程序的主窗口,而不是使用Platform.setImplicitExit(false)
。只要主阶段保持打开状态,FX线程将处于活动状态(并在关闭应用程序时正确处理)
如果您不希望使用FX Stage作为主窗口(因为它需要为您现在拥有的东西使用SwingNode或将您的UI迁移到JavaFX),您可以像这样伪造一个:
@Override
public void start(Stage primaryStage) throws Exception {
YourAppMainWindow mainWindow = new YourAppMainWindow();
// Load your main window Swing Stuff (remember to use
// SwingUtilities.invokeLater() to run inside the Event Dispatch Thread
mainWindow.initSwingUI();
// Now that the Swing stuff is loaded open a "hidden" primary stage
// that will keep the FX Thread alive
primaryStage.setWidth(0);
primaryStage.setHeight(0);
primaryStage.setX(Double.MAX_VALUE);
primaryStage.setY(Double.MAX_VALUE);
primaryStage.initStyle(StageStyle.UTILITY);
primaryStage.show();
}
请记住,伪造主阶段(或将主窗口迁移到FX)将导致更多的代码,而不仅仅是简单地使用Platform.setImplicitExit(false)
和Platform.exit()
无论如何,希望这有帮助 感谢您提供的这段代码:''Platform.setImplicitExit(false)。''我的javafx线程正在消亡(仍然不知道为什么),但您提供的代码修复了它。我的使用案例是,每当有人在dropdownlist中更改某个内容时,我都会删除旧的javafxpanel并添加一个新的。很高兴知道这被认为是一个实际的问题,没有解决的计划。具有隐藏窗口的有趣替代解决方案。现在继续使用Platform.setImplicitExit(false)。
if (initialized.getAndSet(true)) {
// If we've already initialized, just put the runnable on the queue.
runLater(r);
return;
}
@Override
public void start(Stage primaryStage) throws Exception {
YourAppMainWindow mainWindow = new YourAppMainWindow();
// Load your main window Swing Stuff (remember to use
// SwingUtilities.invokeLater() to run inside the Event Dispatch Thread
mainWindow.initSwingUI();
// Now that the Swing stuff is loaded open a "hidden" primary stage
// that will keep the FX Thread alive
primaryStage.setWidth(0);
primaryStage.setHeight(0);
primaryStage.setX(Double.MAX_VALUE);
primaryStage.setY(Double.MAX_VALUE);
primaryStage.initStyle(StageStyle.UTILITY);
primaryStage.show();
}