编程语言
首页 > 编程语言> > 使用JFXPanel Swing interop防止JavaFX线程死亡?

使用JFXPanel Swing interop防止JavaFX线程死亡?

作者:互联网

我将几个JFXPanel嵌入到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的行为可防止JavaFX线程在调用PlatformImpl.checkIdle()时死亡.

当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;
}

我错过了什么吗?

解决方法:

这是一个非常古老的“bug”,随着Platform.setImplicitExit(false)的引入而有所修复.您可以在开放式问题JDK-8090517中阅读开发人员评论.您将看到它具有低优先级,并且可能永远不会得到修复(至少不会很快).

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

如果您不打算使用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)和Platform.exit().

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

标签:java,javafx,swing,javafx-8,interop
来源: https://codeday.me/bug/20191005/1855713.html