![](/img/trans.png)
[英]ERROR! Exception in thread “AWT-EventQueue-0” java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0
[英]Java AWT EventQueue “Not on FX application thread” exception in JavaFX
通過將應用程序從Swing遷移到JavaFX,我遇到了問題。
為了使它盡可能簡單,在我的應用程序開始時,在主要JavaFX類(Main.java)中初始化了JavaFX Stage
。 在初始化結束時(在JFX線程中),我打開了一個Swing定制的JDialog
(目前尚未遷移),要求用戶選擇設備。 在此JDialog
我有一個ObservableModel
,它從用戶在設備選擇JDialog
發送作為參數條目。 我的主要JavaFX類是一個Observer
,我通過重寫update()
函數從所選設備中獲取條目。 到目前為止,一切都很好 。
然后,仍然在update()
函數中,如果用戶未連接,我想打開一個在JavaFX中創建的登錄對話框(在遷移之前為Swing JDialog
)。 當我嘗試打開它時,我得到一個java.lang.IllegalStateException
異常,告訴"Not on FX application thread; currentThread = AWT-EventQueue-0"
現在一些代碼:
JavaFX Main
類Main.java:重要的是,我通過調用openSlpDlg()
打開JDialog
,這將要求用戶選擇設備,選擇的結果將作為參數返回到update()
方法中。
public class Main extends Application implements Observer {
@Override
public void start(Stage stage) {
// init stage
stage.setTitle(Messages.getString("Appli_HaslerST"));
stage.getIcons().add(new Image(getClass().getResourceAsStream("../gifs/application.gif")));
mainContainer = new BorderPane();
Scene root = new Scene(mainContainer, 1200, 1000);
stage.setScene(root);
// init stage content
initialize();
stage.show();
// open that device selection dialog
openSlpDlg();
this.stage = stage;
}
public static void main(String[] args) {
launch(args);
}
@Override
public void update(Observable o, Object arg) {
// called when user has chosen a device in the JDialog
if (arg instanceof Object[]) {
Object[] set = (Object[]) arg;
try {
// con is an Interface that will create the correct
// device JPanel, depending of selection.
// the JPanel is created in constructor of MainGUI
// Start method of device application which creates a
// new MainGUI (JPanel)
// AbstractTabbedPanel is a customed JPanel
AbstractTabbedPanel panel = con.getTabbedPanel();
} catch (Exception e) {
}
}
}
}
外部設備應用程序返回一個JPanel
,它將使用SwingNode
存儲在我的舞台中(工作正常)。 但是在創建此JPanel
,在MainGUI
(如上面的代碼中的注釋中所述),我檢查用戶是否已登錄,如果沒有,則打開登錄對話框,它是JavaFX Dialog<R>
來自外部應用程序的MainGUI
。 由JavaFX Main應用程序在update()
con.getTabbedPane()
調用的con.getTabbedPane()
(在上面的代碼中)將創建一個新的MainGUI
。
public class MainGUI extends JPanel {
public MainGUI() {
// [...] all initalization done in constructor
// test if user connected
if (LocalAccessControl.getInstance().getAccessControl() == null) {
// not connected case
// JavaFX custom login extends from Dialog<LoginInfo>
LoginDlg login;
login = new LoginDlg()
// show login dialog and get a LoginInfo object with
// user info
LoginInfo userInfo = login.showLogin();
}
}
}
並通過LoginInfo userInfo = login.showLogin();
我收到此異常: java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0
java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0
問題是,我需要存儲在userInfo
登錄信息才能繼續在構造函數中構造JPanel
(因此在AWT EventQueue線程中),但是LoginDlg是JavaFX實現的。 我嘗試使用Platform.runLater(new Runnable(){...})
但是它不起作用,而且很正常,因為我當前未使用JavaFX線程,並且不會在FX線程中立即執行(對話框永遠不會顯示並且無法完成登錄,因此JPanel將為空,並顯示警告消息,提示無法建立連接。
我已經在stackoverflow上看到過幾篇關於此異常的文章,但我認為我的案例比較棘手,我找不到解決此問題的好方法。
希望您能夠幫助我。 如果您需要更多詳細信息,請詢問!
將Swing和JavaFX混合使用非常棘手,因為每個工具箱都是單線程的,並且每個都有自己的UI線程,必須在該線程上管理“實時” UI元素。 對於Swing,請參見javax.swing軟件包文檔中的“ Swing的線程策略” 。 對於JavaFX,請參閱Application
文檔的“線程”部分 。 JavaFX嘗試在可能的情況下通過拋出運行時異常來強制執行這些規則(如您所見); Swing不會引發異常。 在這兩種情況下,違反線程規則都有可能使您的應用程序處於不確定狀態,並在應用程序生命周期中的任意時間導致錯誤。
您可以使用SwingUtilities.invokeLater(() -> { ... });
調用AWT事件分發線程上的代碼,然后使用Platform.runLater(() -> { ... });
調用FX應用程序線程上的代碼。 這些調用是異步的 ,因此不會立即執行; 換句話說,緊隨這些調用之后的代碼很可能在提交給UI線程的代碼之前執行。
此外,除非明確設計了這些阻止調用,否則不應在UI線程上進行阻止調用。 例如,如果dialog
是一個FX對話框(或階段),則dialog.showAndWait()
是一個阻塞調用,旨在安全地在FX Application線程上執行; 但是,您不應在AWT事件分發線程上進行阻止以阻止FX對話框輸入的調用。
您尚未在代碼中顯示完整的示例,但看起來您需要以下內容:
@Override
public void start(Stage stage) {
// init stage
stage.setTitle(Messages.getString("Appli_HaslerST"));
stage.getIcons().add(new Image(getClass().getResourceAsStream("../gifs/application.gif")));
mainContainer = new BorderPane();
Scene root = new Scene(mainContainer, 1200, 1000);
stage.setScene(root);
// init stage content
initialize();
stage.show();
// open that device selection dialog: this is a Swing dialog,
// so it must be performed on the AWT event dispatch thread:
SwingUtilities.invokeLater(this::openSlpDlg);
this.stage = stage;
}
您的update()
方法是作為對AWT事件分派線程中用戶輸入的響應而執行的(我假設是從openSlpDlg
顯示的Swing對話框中的操作偵聽器執行); 因此,它及其調用的方法(例如new MainGUI()
在AWT事件分配線程上執行。 因此,您現在需要:
public MainGUI() {
// this method is executed on the AWT event dispatch thread
// [...] all initalization done in constructor
// test if user connected
if (LocalAccessControl.getInstance().getAccessControl() == null) {
// not connected case
// JavaFX custom login extends from Dialog<LoginInfo>
// Tricky part. We need to create a JavaFX dialog (so must be
// done on the FX Application Thread), show it and wait for the result
// (so make a blocking call, which needs to be on a background thread),
// and process the result back on the AWT thread.
// task to execute on the FX Application Thread, returning a LoginInfo:
FutureTask<LoginInfo> getLoginTask = new FutureTask<>(() -> {
LoginDlg login;
login = new LoginDlg()
// show login dialog and get a LoginInfo object with
// user info
return login.showLogin();
});
// execute the task on the FX Application Thread:
Platform.runLater(getLoginTask);
// now create a background thread that waits for the login task to complete,
// and processes the result back on the AWT event dispatch thread:
Thread waitForLoginThread = new Thread(() -> {
try {
final LoginInfo userLogin = getLoginTask.get();
SwingUtilities.invokeLater(() -> {
// process userLogin here...
});
} catch (InterruptedException exc) {
throw new Error("Unexpected interruption waiting for login");
} catch (ExecutionException exc) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Error getting login info", exc);
}
});
waitForLoginThread.start();
}
}
如果您確實要阻塞MainGUI
構造函數,直到關閉FX對話框,則可以通過等待提交給FX Application Thread的任務直接在那里完成(而不是在后台線程中)來完成:
public MainGUI() {
// this method is executed on the AWT event dispatch thread
// [...] all initalization done in constructor
// test if user connected
if (LocalAccessControl.getInstance().getAccessControl() == null) {
// not connected case
// JavaFX custom login extends from Dialog<LoginInfo>
// Tricky part. We need to create a JavaFX dialog (so must be
// done on the FX Application Thread), show it and wait for the result
// (so make a blocking call, which needs to be on a background thread),
// and process the result back on the AWT thread.
// task to execute on the FX Application Thread, returning a LoginInfo:
FutureTask<LoginInfo> getLoginTask = new FutureTask<>(() -> {
LoginDlg login;
login = new LoginDlg()
// show login dialog and get a LoginInfo object with
// user info
return login.showLogin();
});
// execute the task on the FX Application Thread:
Platform.runLater(getLoginTask);
// wait for task submitted to FX Application Thread to complete.
// note this blocks the AWT event dispatch thread:
try {
final LoginInfo userLogin = getLoginTask.get();
// process userLogin here...
} catch (InterruptedException exc) {
Thread.currentThread().interrupt();
} catch (ExecutionException exc) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Error getting login info", exc);
}
}
}
我不推薦這種方法:它將使Swing用戶界面無響應,並可能導致不良的用戶體驗。 通常最好對應用程序進行結構設計,以便可以在未完成登錄的情況下顯示MainGUI
,然后在登錄完成后進行更新。 否則將MainGUI
在登錄完成之前根本不調用MainGUI
構造函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.