[英]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.