0

I'm executing a script from my application and updating a text area with the output via a change listener on the Task value property but not all lines show up.

this.valueProperty().addListener(new ChangeListener<String>() {

    @Override
    public void changed(ObservableValue observable, String oldValue, String newValue) {
        if (!(newValue == null)) {
            output.appendText(newValue + "\n");
        }
    }

});

My issue is essentially answered in JavaFX ChangeListener not always working but I have a follow up question.

Sleeping the Task before or after calling updateValue() seems to have my desired output of continuously appending the result but I don't understand how. As the sleep time gets lower, the result gets more and more incomplete. If the Task is slept, wouldn't it pick up right where it left off and I wouldn't see any difference in behavior? Because the coalesced value property hasn't changed?

Or does sleeping the thread guarantee that the value won't be updated for another 250 milliseconds and the UI thread is updating in the meantime? And that gives the UI thread enough time to capture every line? That doesn't seem like a very reliable way to update because how would I know how much time is needed to get the full output?

    while ((line = outputReader.readLine()) != null) {
        updateValue(line);
        Thread.sleep(250);
    }

I tried updating the TextArea directly via Platform.runLater after reading each line but I get the error "local variable referenced from an inner class" and I couldn't figure out how to get around that. The TexArea is an instance variable of my extended Task class and it's not final since I'm appending strings so I don't know a way around it.

atye
  • 87
  • 1
  • 13
  • Try to call `updateValue()` with `Platform.runLater()` (i.e. `Platform.runLater(() -> updateValue(line))`) – Jai Mar 14 '19 at 05:29
  • I get the "local variable referenced from an inner class" error with that. Maybe I need to make the text area a separate class rather than a variable in my customer Task class. – atye Mar 14 '19 at 05:51
  • the local variable error had nothing to do with the text area. It was because the "line" variable wasn't made into a final variable, like in fabian's answer. – atye Mar 14 '19 at 15:16

1 Answers1

0

Task.updateValue makes sure an update will happen in the future. Since scheduling those updates is expensive, it just makes sure there will be an update to the last value passed. It works similar to this

void updateValue(T value) {
    setNewValue(value);
    if (lastUpdateFinished()) {
        Platform.runLater(() -> doUpdate());
    }
}

Where lastUpdateFinished returns false if and only if there is an update scheduled that has yet to retrieve the new value. Theoretically no amount of sleeping is a guarantee to prevent issues, but in practice the longer you sleep, the more likely lastUpdateFinished() yields true for every call of updateValue...


In your case Platform.runLater needs to be used multiple times to ensure the application thread sees every single update:

String line;
while ((line = outputReader.readLine()) != null) {
    final String lineFinal = line + "\n";
    Platform.runLater(() -> output.appendText(lineFinal));
}

Note that this may slow down the JavaFX, if updates come in too frequently.

fabian
  • 80,457
  • 12
  • 86
  • 114