我的任务可能需要几秒钟到几分钟,当我点击按钮执行任务时,它会运行任务,但并不总是禁用按钮A
并启用按钮{{ 1}}。
以下是我正在使用的代码:
B
如果我注释掉@FXML
public void onExecute(ActionEvent event){
btnExecute.setDisable(true);
btnStopExec.setDisable(false);
new Thread(){
@Override
public void run(){
Platform.runLater(() -> {
QueryTable qt = new QueryTable(currentMysqlConn, currentDatabase);
qt.setTabPane(resultsTabPane);
qt.setQuery(queries);
qt.executeQueries();
btnExecute.setDisable(false);
btnStopExec.setDisable(true);
});
}
}.start();
}
按钮Platform.runLater()
中禁用的按钮被禁用,按钮A
被启用,但B
运行后。为什么这有时会起作用而不是其他人呢?
答案 0 :(得分:3)
根据Javadocs for Platform.runLater(...)
,它
在FX应用程序线程上运行指定的Runnable
因此,后台线程所做的唯一事情是安排所有耗时的数据库工作在FX应用程序线程上运行:您的后台线程基本上是多余的,并且在数据库工作运行时您的UI将无响应。
如果在调用btnExecute.setDisable(true);
和您定义执行的runnable之间呈现帧,则会看到禁用状态更改。如果没有,则所有代码在同一帧渲染(*)期间执行,因此您永远不会看到禁用状态更改。
Platform.runLater()
来更新UI。所以你可以按照以下方式完成这项工作:
@FXML
public void onExecute(){
btnExecute.setDisable(true);
btnStopExec.setDisable(false);
new Thread(){
@Override
public void run(){
QueryTable qt = new QueryTable(currentMysqlConn, currentDatabase);
qt.setTabPane(resultsTabPane);
qt.setQuery(queries);
qt.executeQueries();
Platform.runLater(() -> {
btnExecute.setDisable(false);
btnStopExec.setDisable(true);
});
}
}.start();
}
Platform.runLater(...)
对外汇工作来说是一种非常低级的方法。 javafx.concurrent
包为此定义了更高级别的API:特别是Task
类封装后台任务并提供将在FX应用程序线程上执行的回调,以便您可以更新UI。 Javadocs有很多例子,但你可以这样做:
@FXML
public void onExecute(){
btnExecute.setDisable(true);
btnStopExec.setDisable(false);
Task<Void> databaseTask = new Task<Void>() {
@Override
public void call(){
QueryTable qt = new QueryTable(currentMysqlConn, currentDatabase);
qt.setTabPane(resultsTabPane);
qt.setQuery(queries);
qt.executeQueries();
return null ;
}
};
databaseTask.setOnSucceeded( event -> {
btnExecute.setDisable(false);
btnStopExec.setDisable(true);
});
new Thread(databaseTask).start();
}
(*)这是一个有点不精确的陈述,但它在质量上是正确的。从技术上讲,渲染线程在FX应用程序线程上执行操作时阻塞。 (这两个不是相同的线程,但它们之间有很大的同步。)因此,在QueryTable qt = new QueryTable(...);
和btnStopExec.setDisable(true);
的调用之间无法呈现帧。帧可以在btnStopExec.setDisable(false);
和runnable的执行之间(即QueryTable qt = new QueryTable(...);
之前)进行渲染。如果渲染了这样的帧,则会看到禁用状态发生变化;如果没有,你就不会。是否发生这种情况仅仅是关于“脉冲”(帧渲染)的调用时间,这些目标是每1/60秒发生一次。