使用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();
}