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