簡體   English   中英

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

[英]Prevent JavaFX thread from dying with JFXPanel Swing interop?

我將幾個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的行為可防止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()

無論如何,希望這有幫助!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM