使用 JFXPanel Swing 互操作防止 JavaFX 线程死亡?

2023-11-22

我嵌入了几个JFXPanels当 JFXPanel 不再可见时,JavaFX 线程就会终止。这是有问题的,因为创建另一个JFXPanelJavaFX 线程死亡后不会启动另一个 JavaFX 线程,因此JFXPanel将是空白的。

据我所知,JFXPanel ctor 通过调用以下命令启动 JavaFX 线程:

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

后来有一次JFXPanel有一个父组件addNotify调用方法,该方法调用registerFinishListener它注册了一个PlatformImpl.FinishListener() with PlatformImpl。登记行为FinishListener防止 JavaFX 线程在以下情况下死亡PlatformImpl.checkIdle()叫做。

When a JFXPanel不再可见其removeNotify方法是调用它调用deregisterFinishListener():

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

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

我发现解决此问题的唯一方法是致电Platform.setImplicitExit(false)在 Swing 应用程序开始时,以便 JavaFX 线程永远不会自动终止。此修复需要调用Platform.exit()当应用程序退出时,否则 JavaFX 线程将阻止进程停止。

这似乎是 JavaFX-Swing 互操作中的一个错误,或者至少应该修改互操作文档以通过讨论来解决这个问题Platform.setImplicitExit(false).

另一种解决方案是允许在创建另一个 JFXPanel 但被阻止时创建新的 JavaFX 线程PlatformImpl.startup(Runnable):

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

我错过了什么吗?


这是一个非常古老的“错误”,随着Platform.setImplicitExit(false)。您可以在开放问题中阅读开发者评论JDK-8090517。正如您将看到的,它的优先级较低,并且可能永远不会得到修复(至少不会很快)。

您可能想尝试而不是使用另一种解决方案Platform.setImplicitExit(false)就是扩展当前Main类中的Application类,并使用主Stage来显示应用程序的主窗口。只要主舞台保持打开状态,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) and Platform.exit()因此。

无论如何,希望这有帮助!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 JFXPanel Swing 互操作防止 JavaFX 线程死亡? 的相关文章

随机推荐