简体   繁体   English

使用JFXPanel Swing interop防止JavaFX线程死亡?

[英]Prevent JavaFX thread from dying with JFXPanel Swing interop?

I'm embedding several JFXPanels into a Swing app and the JavaFX thread dies when the JFXPanels are no longer visible. 我将几个JFXPanels嵌入到Swing应用程序中,当JFXPanel不再可见时,JavaFX线程就会死掉。 This is problematic because creating another JFXPanel after the JavaFX thread dies will not start another JavaFX thread and thus the JFXPanel will be blank. 这是有问题的,因为在JavaFX线程死后创建另一个JFXPanel将不会启动另一个JavaFX线程,因此JFXPanel将为空。

From what I can tell the JFXPanel ctor starts the JavaFX thread by calling: 据我所知,JFXPanel ctor通过调用以下命令启动JavaFX线程:

PlatformImpl.startup(new Runnable() {
   @Override public void run() {
      // No need to do anything here
   }
});

Later on once the JFXPanel has a parent component its addNotify method is called which calls registerFinishListener which registers a PlatformImpl.FinishListener() with PlatformImpl . 稍后,一旦JFXPanel具有父组件,就调用其addNotify方法,该方法调用registerFinishListener ,该方法使用PlatformImpl注册PlatformImpl.FinishListener() The act of registering the FinishListener prevents the JavaFX thread from dying when PlatformImpl.checkIdle() is called. 注册FinishListener的行为可防止JavaFX线程在调用PlatformImpl.checkIdle()时死亡。

When a JFXPanel is no longer visible its removeNotify method is call which calls deregisterFinishListener() : JFXPanel不再可见时,其removeNotify方法是调用deregisterFinishListener()调用:

private synchronized void deregisterFinishListener() {
    if (instanceCount.decrementAndGet() > 0) {
        // Other JFXPanels still alive
        return;
    }
    PlatformImpl.removeListener(finishListener);
    finishListener = null;
}

When instanceCount is zero the FinishListener is removed which causes PlatformImpl to call PlatformImpl.tkExit in the following code which causes the JavaFX thread to die: instanceCount为零FinishListener被除去这导致PlatformImpl调用PlatformImpl.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();
    }
}

The only way I've found to fix this issue is to call Platform.setImplicitExit(false) at the begining of the Swing app so that the JavaFX thread never dies automatically. 我发现修复此问题的唯一方法是在Swing应用程序开始时调用Platform.setImplicitExit(false) ,以便JavaFX线程永远不会自动死亡。 This fix requires a call the Platform.exit() when the application exits otherwise the JavaFX thread will prevent the process from stopping. 此修复需要在应用程序退出时调用Platform.exit() ,否则JavaFX线程将阻止进程停止。

This seems like a bug in JavaFX-Swing interop or at the very least the interop documentation should be modified to address this by discussing Platform.setImplicitExit(false) . 这似乎是JavaFX-Swing互操作中的一个错误,或者至少应该通过讨论Platform.setImplicitExit(false)修改互操作文档来解决这个问题。

Another solution would be to allow creation of a new JavaFX thread when another JFXPanel is created but that is blocked by PlatformImpl.startup(Runnable) : 另一个解决方案是允许在创建另一个JFXPanel但是被PlatformImpl.startup(Runnable)阻止时创建新的JavaFX线程:

if (initialized.getAndSet(true)) {
   // If we've already initialized, just put the runnable on the queue.
   runLater(r);
   return;
}

Am I missing something? 我错过了什么吗?

This is a really old "bug" that was somewhat fixed with the introduction of Platform.setImplicitExit(false) . 这是一个非常古老的“bug”,随着Platform.setImplicitExit(false)的引入而有所修复。 You can read the developers comments in the open issue JDK-8090517 . 您可以在公开发行的JDK-8090517中阅读开发人员的评论。 As you will see it has a low priority and probably will never get fixed (at least not soon). 正如您将看到它具有低优先级,并且可能永远不会得到修复(至少不会很快)。

Another solution you might want to try instead of using Platform.setImplicitExit(false) is to extend the Application class in your current Main class and use the primary Stage to display the application's main window. 您可能想要尝试而不是使用Platform.setImplicitExit(false)另一个解决方案是在当前Main类中扩展Application类,并使用主Stage显示应用程序的主窗口。 As long as the primary Stage remains open the FX Thread will be alive (and dispose correctly when you close your app). 只要主要舞台保持打开状态,FX Thread将处于活动状态(并在关闭应用程序时正确处理)。

If you aren't looking to use an FX Stage as your main window (since it would require to use a SwingNode for what you have now or migrate your UI to JavaFX) you can always fake one like this: 如果您不打算使用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();
}

Keep in mind that faking a primary stage (or migrating your main window to FX) will end in more code than simply using Platform.setImplicitExit(false) and Platform.exit() accordingly. 请记住,伪造主要阶段(或将主窗口迁移到FX)将以更多代码结束,而不是简单地使用Platform.setImplicitExit(false)Platform.exit()

Anyway, hope this helps! 无论如何,希望这有帮助!

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

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