任务不更新局部变量

时间:2019-02-20 20:49:08

标签: javafx concurrency

我正在使用一个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;
}

2 个答案:

答案 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的值。无法保证您致电Taskprintln(text)会完成。实际上,您的Task很可能尚未完成。但这不是唯一的问题。

readFileprintln的调用都在同一个线程上完成,在本例中为 JavaFX Application Thread 。这里的问题是,传递给EventHandler的{​​{1}}也将在FX线程上被调用。在内部实现此目标的方式是通过setOnSucceeded调用来安排,该操作计划将FX线程的动作安排在将来的某个时间段 中运行。 FX线程正在执行Platform.runLater时不会发生这种情况,必须等待该方法返回。

这一切意味着openMenuItemClick 直到调用setCurrentText 之后才会运行。但是到第二次调用println时,openMenuItemClick将被设置为 1 。因此,您第二次看到的实际上是第一次text的结果。

如果您想在Task完成后对text进行操作,则应在TaskonSucceeded处理程序中执行。或者,您可以将onFailed设为text并观察它的变化。


1。从技术上讲,它已可能已设置。仍然不能保证StringProperty到那时已经完成。