我正在使用一个Task读取文本文件,当用户单击“打开文件”菜单时调用该任务,应该读取该文本文件,然后更新局部变量“ text”,问题第一次尝试时发生,如果我打开文件,什么也没有发生,并且文本字符串的值保持原样,如果我再次打开任何文件 ,一切都按预期工作,我无法找出原因。
具有任务的方法
private void readFile(File file){
Task<String> task = new Task<String>() {
@Override
protected String call() {
List<String> list = EditorUtils.readFromFile(file);
String str = list.stream().collect(Collectors.joining("\n"));
return str;
}
};
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
setCurrentText(task.getValue());
}
});
task.setOnFailed(e -> setCurrentText("FAILED"));
Thread t = new Thread(task);
t.setDaemon(true);
t.start();
}
SetCurrentText
private void setCurrentText(String text){
this.text = text;
}
控制器的方法
@FXML
void openMenuItemClick(ActionEvent event) {
fileChooser.setTitle("title");
fileChooser.getExtensionFilters().add
(new FileChooser.ExtensionFilter("TXT files (*.txt)", "*.txt"));
File file = fileChooser.showOpenDialog(open.getParentPopup().getScene().getWindow());
if (file != null){
readFile(file);
System.out.println(text); //prints null since "text" isn't initialized yet
}
}
EditorUtils#readFromFile
public static List<String> readFromFile(File file){
List<String> lines = new ArrayList<>();
try {
lines = Files.readAllLines(Paths.get(file.getPath()), StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
return lines;
}
答案 0 :(得分:2)
当使用多个线程时,这是完全正常的行为。您可以从在后台线程上运行的任务访问文件。完成后,此任务将触发JavaFX应用程序线程上的更新。
到readFile
返回时,任务可能尚未完成。 Task
使用Platform.runLater
执行onSucceeded
处理程序的事实导致即使在{{1}之前读取文件,该处理程序也不会在openMenuItemClick
方法完成之前被调用。 }}。
如果您需要根据System.out.println
的结果更新GUI,则应从事件处理程序中进行更新。更新Task
字段的代码在text
语句之后运行。第二次启动任务时,将打印第一次单击菜单项而不是新菜单项时启动的任务结果。您可以通过将System.out.println(text);
移动到println
方法的开头来验证这一点。
答案 1 :(得分:2)
您的readFile
方法创建一个Task
,将其赋予一个Thread
,启动Thread
,然后返回。然后,您尝试立即打印出text
的值。无法保证您致电Task
时println(text)
会完成。实际上,您的Task
很可能尚未完成。但这不是唯一的问题。
对readFile
和println
的调用都在同一个线程上完成,在本例中为 JavaFX Application Thread 。这里的问题是,传递给EventHandler
的{{1}}也将在FX线程上被调用。在内部实现此目标的方式是通过setOnSucceeded
调用来安排,该操作计划将FX线程的动作安排在将来的某个时间段 中运行。 FX线程正在执行Platform.runLater
时不会发生这种情况,必须等待该方法返回。
这一切意味着openMenuItemClick
直到调用setCurrentText
之后才会运行。但是到第二次调用println
时,openMenuItemClick
将被设置为 1 。因此,您第二次看到的实际上是第一次text
的结果。
如果您想在Task
完成后对text
进行操作,则应在Task
或onSucceeded
处理程序中执行。或者,您可以将onFailed
设为text
并观察它的变化。
1。从技术上讲,它已可能已设置。仍然不能保证StringProperty
到那时已经完成。