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.
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
}
});
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.
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;
}
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();
}
}
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.
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)
.
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;
}
Am I missing something?