使用JFXPanel Swing互操作防止JavaFX线程死亡? [英] Prevent JavaFX thread from dying with JFXPanel Swing interop?

查看:491
本文介绍了使用JFXPanel Swing互操作防止JavaFX线程死亡?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我将几个 JFXPanels 嵌入到Swing应用程序中,当JFXPanel不再可见时,JavaFX线程就会死掉。这是有问题的,因为在JavaFX线程死后创建另一个 JFXPanel 将不会启动另一个JavaFX线程,因此 JFXPanel 将是空白。

I'm embedding several JFXPanels into a Swing app and the JavaFX thread dies when the JFXPanels are no longer visible. 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.

我可以告诉JFXPanel ctor通过调用来启动JavaFX线程:

From what I can tell the JFXPanel ctor starts the JavaFX thread by calling:

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()时死亡。

Later on once the JFXPanel has a parent component its addNotify method is called which calls registerFinishListener which registers a PlatformImpl.FinishListener() with PlatformImpl. The act of registering the FinishListener prevents the JavaFX thread from dying when PlatformImpl.checkIdle() is called.

JFXPanel 不再可见时,其 removeNotify 方法即被调用调用 deregisterFinishListener()

When a JFXPanel is no longer visible its removeNotify method is call which calls 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线程死亡:

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:

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线程将阻止进程停止。

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. This fix requires a call the Platform.exit() when the application exits otherwise the JavaFX thread will prevent the process from stopping.

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

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).

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

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):

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

我错过了什么?

推荐答案

这是一个非常古老的错误,在引入 Platform.setImplicitExit(false)时有所修复。您可以在公开的 JDK-8090517 中阅读开发人员的评论。正如您将看到它具有低优先级并且可能永远不会得到修复(至少不会很快)。

This is a really old "bug" that was somewhat fixed with the introduction of Platform.setImplicitExit(false). You can read the developers comments in the open issue JDK-8090517. As you will see it has a low priority and probably will never get fixed (at least not soon).

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

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. As long as the primary Stage remains open the FX Thread will be alive (and dispose correctly when you close your app).

如果你不打算使用FX将舞台作为主窗口(因为现在需要使用SwingNode或将UI迁移到JavaFX),你总是可以假装这样:

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:

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

请记住伪造主要阶段(或迁移主窗口) to FX)将以更多代码结束而不仅仅是简单地使用 Platform.setImplicitExit(false) Platform.exit()

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.

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

Anyway, hope this helps!

这篇关于使用JFXPanel Swing互操作防止JavaFX线程死亡?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆