Задача не обновляет локальную переменную - PullRequest
0 голосов
/ 20 февраля 2019

Я использую задачу для чтения текстового файла, задача вызывается, когда пользователь щелкает меню «открыть файл», он должен прочитать текстовый файл, а затем обновить локальную переменную «текст», проблемапроисходит с первой попытки, если я открываю файл, ничего не происходит, и значение текстовой строки остается прежним, если я открываю любой файл снова , все работает, как ожидалось, я не смог найтиПричина этого.

Метод, который имеет задачу

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 голосов
/ 21 февраля 2019

Ваш метод readFile создает Task, передает его Thread, запускает Thread и возвращает.Затем вы пытаетесь немедленно распечатать значение text.Нет гарантии, что ваш Task будет завершен к тому времени, когда вы позвоните println(text).На самом деле, очень вероятно, что ваш Task еще не завершен.Но это не единственная проблема.

Вызов readFile и println выполняются в одном потоке - в данном случае Поток приложения JavaFX .Проблема здесь в том, что EventHandler, который вы передаете setOnSucceeded, также будет вызываться в потоке FX.Это достигается внутренним способом с помощью вызова Platform.runLater, который планирует действие с потоком FX для запуска в будущем .Это не может произойти, пока поток FX выполняет openMenuItemClick и должен ждать, пока метод не вернется.

Все это означает, что setCurrentText никогда не будет работать до тех пор, пока не будет вызван println.Но во второй раз, когда вызывается openMenuItemClick, text будет установлено 1 .Так что то, что вы видите во второй раз, на самом деле является результатом первого Task.

Если вы хотите что-то сделать с text после завершения Task, то вы должны сделать это внутри onSucceeded или onFailed обработчик.Или вы можете сделать text a StringProperty и наблюдать за изменениями.


1.Технически, может быть установлено .До сих пор нет никаких гарантий, что Task завершится к тому времени.

0 голосов
/ 21 февраля 2019

Это совершенно нормальное поведение при использовании нескольких потоков.Вы получаете доступ к файлу из задачи, выполняющейся в фоновом потоке.По завершении этой задачи запускается обновление потока приложения JavaFX.

К тому времени, когда readFile возвращает, задача может быть не выполнена.Тот факт, что Task использует Platform.runLater для выполнения обработчика onSucceeded, приводит к тому, что этот обработчик никогда не вызывается до завершения метода openMenuItemClick, даже если файл читается до достижения System.out.println.

Если вам нужно обновить графический интерфейс на основе результата Task, вы должны сделать это из обработчика событий.Обновление кода в поле text выполняется после оператора System.out.println(text);.При втором запуске задания вы распечатываете результаты задания, запущенного при первом нажатии элемента меню, а не нового.В этом можно убедиться, переместив println в начало метода openMenuItemClick.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...