我有一个简单的应用程序可以更新Backgroud中的数据,当它更新时会禁用所有其他按钮,并使TextArea显示进度。
步骤 -
禁用mainUI中的所有其他按钮(按钮名称:plotButton)
启用TextArea以显示更新已开始(TextArea name:infoLogTextArea)
然后只启动更新方法(update()抛出异常)。
以下是以下代码 -
@FXML
public void handleUpdateButton() {
infoLogTextArea.setVisible(true);
infoLogTextArea.appendText("Please wait while downloading data from internet.....\n");
plotButton.setDisable(true);
updateButton.setDisable(true);
if(c!=null) {
Runnable task = new Runnable() {
@Override
public void run() {
// Thread.sleep(10000); -> sleep for 10secs
Platform.runLater(new Runnable() {
@Override
public void run() {
try {
c.updateData();
infoLogTextArea.appendText(c.getErrorLog().toString());
plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
infoLogTextArea.appendText("Successfully updated the data from Internet\n");
}catch (IOException e) {
infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
}
finally {
plotButton.setDisable(false);
updateButton.setDisable(false);
}
}
});
}
};
new Thread(task).start();
}else {
System.out.println("c not initialized");
}
}
现在代码运行良好,但有时步骤1和2没有执行,它启动步骤3(更新),可以冻结程序。 现在,如果我将Thread.sleep(10秒)置于步骤2和3之间,那么它将完全正常。 (在代码中注释)
但是有人可以解释一下后面会发生什么,以及为什么platform.runLater()不能一直工作?
答案 0 :(得分:3)
JavaFX应用程序在Application线程上运行,该线程处理所有UI元素。这意味着如果单击按钮A并单击该按钮启动方法A需要5秒钟才能完成,然后单击该按钮后一秒钟,您尝试单击按钮B启动方法B,方法B不会启动直到方法A结束。或者按钮B甚至可能无法工作直到方法A完成,我对那里的细节有点模糊。
阻止应用程序冻结的好方法是使用Threads。要解决上述问题,单击按钮A将启动启动新线程的方法A.然后线程可以在完成所需的时间内完成而不会锁定UI并阻止您单击按钮B.
现在,在方法A中说一些需要在应用程序线程上的东西,例如,它更新了一个UI组件,比如Label或TextField。然后在方法A中的线程内部,您需要将影响UI的部分放入Platform.runLater()
,以便它将在应用程序线程上与其余UI一起运行。
这对你的例子意味着你有两个选择
1.根本不使用线程,因为您不希望用户在更新发生时与UI进行交互。
2.将c.updateData()
移出Platform.runLater()
,如下所示:
Runnable task = new Runnable() {
@Override
public void run() {
c.updateData();
Platform.runLater(new Runnable() {
@Override
public void run() {
try {
infoLogTextArea.appendText(c.getErrorLog().toString());
plotLabel.setText(c.getCityData().size()+" cities found and updated from internet");
infoLogTextArea.appendText("Successfully updated the data from Internet\n");
}catch (IOException e) {
infoLogTextArea.setText("Couldnot update the data from web: "+e.getMessage()+"\n");
}
finally {
plotButton.setDisable(false);
updateButton.setDisable(false);
}
}
});
}
};
其中任何一个都可以工作,但是你现在正在做的是你在应用程序线程上,然后你启动另一个线程,其唯一目的是在应用程序线程上运行某些东西。
答案 1 :(得分:1)
Platform类的文档很好地解释了所有内容:
public static void runLater(Runnable runnable)
在JavaFX Application Thread上运行指定的Runnable 未指定时间未来。这种方法可以从中调用 任何线程,都会将Runnable发布到事件队列然后返回 立即给来电者。 Runnables按顺序执行 他们被张贴了。传递给runLater方法的runnable将是 在任何Runnable传入后续调用之前执行 runLater。如果在JavaFX运行时之后调用此方法 shutdown,将忽略该调用:Runnable将不会被执行 并且不会抛出异常。 注意:应用程序应避免 JavaFX充斥着太多未决的Runnables。否则, 应用程序可能会无响应。鼓励申请 将多个操作批处理为更少的runLater调用。的另外下, 长时间运行的操作应该在后台线程中完成 可能,为GUI操作释放JavaFX应用程序线程。
在FX运行时之前,不得调用此方法 初始化。对于扩展Application的标准JavaFX应用程序, 并使用Java启动器或其中一个启动方法 应用程序类启动应用程序,FX运行时是 在加载Application类之前由启动程序初始化。
因此,使用runLater只更新非JavaFX线程上的任何UI元素,并将任何繁重的工作留在后台线程上。