【问题标题】:Prevent JavaFX thread from dying with JFXPanel Swing interop?防止 JavaFX 线程因 JFXPanel Swing 互操作而死亡?
【发布时间】:2014-10-01 07:48:38
【问题描述】:

我将几个 JFXPanels 嵌入到 Swing 应用程序中,当 JFXPanel 不再可见时,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()。注册FinishListener 的行为可以防止在调用PlatformImpl.checkIdle() 时JavaFX 线程死亡。

JFXPanel 不再可见时,它的removeNotify 方法被调用,该方法调用deregisterFinishListener()

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

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

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

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

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

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

我错过了什么吗?

【问题讨论】:

  • 感谢您提供这段代码:'''Platform.setImplicitExit(false)。''' 我的 javafx 线程正在死去(仍然不知道为什么),但您提供的代码修复了它。我的用例是,每当有人更改下拉列表中的某些内容时,我都会删除旧的 javafxpanel 并添加一个新的。

标签: java swing javafx interop javafx-8


【解决方案1】:

这是一个非常古老的“错误”,在引入Platform.setImplicitExit(false) 后得到了一定程度的修复。您可以在未解决的问题JDK-8090517 中阅读开发人员 cmets。正如您将看到的,它的优先级很低,可能永远不会得到修复(至少不会很快)。

您可能想要尝试而不是使用Platform.setImplicitExit(false) 的另一个解决方案是在您当前的 Main 类中扩展 Application 类并使用主 Stage 来显示应用程序的主窗口。只要主 Stage 保持打开状态,FX 线程就会处于活动状态(并在您关闭应用时正确处理)。

如果您不希望将 FX 舞台用作主窗口(因为它需要使用 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() 结束更多代码。

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

【讨论】:

  • 感谢您链接 JDK 错误。很高兴知道它被认为是一个没有计划修复的实际问题。带有隐藏窗口的有趣替代解决方案。暂时坚持使用 Platform.setImplicitExit(false)。
猜你喜欢
  • 2018-02-22
  • 2021-10-16
  • 1970-01-01
  • 1970-01-01
  • 2020-06-14
  • 2023-03-29
  • 2011-03-01
  • 2021-03-04
  • 1970-01-01
相关资源
最近更新 更多