...这是在它的所有属性 - 包括它的值 - 之后更新了吗?
用例是一个
的任务Task实现的片段(最后的完整示例):
@Override
protected ObservableList<Rectangle> call() throws Exception {
ObservableList<Rectangle> results = FXCollections.observableArrayList();
for (int i=0; i<=count; i++) {
// do fill list
//...
try {
Thread.sleep(200);
} catch (InterruptedException interrupted) {
if (isCancelled()) {
// do update value on cancelled
updateValue(results);
break;
}
}
}
return results;
}
它的用途:
我的方法是听取其状态属性并解除状态变为已成功或取消。前者工作得很好,后者没有,因为在收到取消时,该值尚未更新,因此未设置项目。
// working ... but when to unbind?
table.itemsProperty().bind(task.valueProperty());
task.stateProperty().addListener((src, ov, nv) -> {
if (Worker.State.SUCCEEDED == nv ) {
// this is fine because implementation in TaskCallable first
// updates the value (with the result it got from T call())
// then updates state
LOG.info("succeeded" + task.getValue());
table.itemsProperty().unbind();
} else if (Worker.State.CANCELLED == nv) {
LOG.info("receiving cancelled " + task.getValue());
// can't unbind here, value not yet updated
// table.itemsProperty().unbind();
}
});
因此,如果取消,这将留下我仍然绑定的属性或空表。感觉就像我做错了什么。或核心任务impl没有预期的那么有用?这意味着由于无法安全清理,我们无法绑定到value属性(也没有任何其他类似的进程)(这里使用表项只是一个例子,因为它很容易看,所有类型的属性相同)。
问题是,如何正确地做/克服限制?
完整的例子:
public class TaskValueBinding extends Application {
private Parent createListPane() {
Task<ObservableList<Rectangle>> task = createListTask();
Thread thread = new Thread(task);
thread.setDaemon(true);
TableView<Rectangle> table = new TableView<>();
TableColumn<Rectangle, Double> xCol = new TableColumn<>("X");
xCol.setCellValueFactory(new PropertyValueFactory<>("x"));
TableColumn<Rectangle, Double> yCol = new TableColumn<>("Y");
yCol.setCellValueFactory(new PropertyValueFactory<>("y"));
table.getColumns().addAll(xCol, yCol);
// working ... but when to unbind?
table.itemsProperty().bind(task.valueProperty());
task.stateProperty().addListener((src, ov, nv) -> {
if (Worker.State.SUCCEEDED == nv ) {
// this is fine because implementation in TaskCallable first
// updates the value (with the result it got from T call())
// then updates state
LOG.info("succeeded" + task.getValue());
table.itemsProperty().unbind();
} else if (Worker.State.CANCELLED == nv) {
LOG.info("receiving cancelled " + task.getValue());
// can't unbind here, value not yet updated
// table.itemsProperty().unbind();
}
});
Label messageLabel = new Label("Message: ");
Label message = new Label();
message.textProperty().bind(task.messageProperty());
Label progressAsText = new Label();
Label progressLabel = new Label("Progress: ");
progressAsText.textProperty().bind(task.progressProperty().asString());
ProgressBar progress = new ProgressBar();
progress.progressProperty().bind(task.progressProperty());
Button start = new Button("Start");
start.setOnAction(e -> {
start.setDisable(true);
thread.start();
});
Button cancel = new Button("Cancel");
cancel.setOnAction(e -> task.cancel());
cancel.disableProperty().bind(task.runningProperty().not());
int row = 0;
GridPane grid = new GridPane();
grid.add(table, 0, row++, 20, 1);
grid.add(messageLabel, 0, row);
grid.add(message, 1, row++);
grid.add(progressLabel, 0, row);
grid.add(progressAsText, 1, row++);
grid.add(progress, 0, row++, 2, 1);
grid.add(start, 0, row);
grid.add(cancel, 1, row++);
return grid;
}
private Task<ObservableList<Rectangle>> createListTask() {
Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
@Override
protected ObservableList<Rectangle> call() throws Exception {
updateMessage("Creating Rectangles ...");
ObservableList<Rectangle> results = FXCollections.observableArrayList();
String message = "finished";
int count = 10;
for (int i=0; i<=count; i++) {
if (isCancelled()) {
updateValue(results);
// when do we get here?
message = "cancelled";
break;
}
Rectangle r = new Rectangle(10, 10);
r.setX(10 * i);
results.add(r);
updateProgress(i, count);
// Now block the thread for a short time, but be sure
// to check the interrupted exception for cancellation!
try {
Thread.sleep(200);
} catch (InterruptedException interrupted) {
if (isCancelled()) {
updateValue(results);
message = "interrupted";
break;
}
}
}
updateMessage(message);
return results;
}
};
return task;
}
@Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createListPane()));
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TaskValueBinding.class.getName());
}
答案 0 :(得分:1)
取消任务会立即触发state
属性的更新。如果从应用程序中取消线程Platfrom.runLater
未用于此目的,但cancel
方法的调用会立即更新状态。这导致状态在任何updateValue
调用使用value
更新Platform.runLater
属性之前发生更改。
Task
并非旨在允许部分结果,因此您需要实现自定义逻辑以适应此目的。根据您的需要,当任务以任何方式完成时,您可以将Task
子类化为触发自定义事件。
public abstract class PartialResultTask<T> extends Task<T> {
// handler triggered after last change of value
private Runnable onDone;
public Runnable getOnDone() {
return onDone;
}
public void setOnDone(Runnable onDone) {
this.onDone = onDone;
}
protected abstract T calculateResult() throws Exception;
private void onDone() {
if (onDone != null) {
Platform.runLater(onDone);
}
}
@Override
protected final T call() throws Exception {
try {
T result = calculateResult();
updateValue(result); // update value to the final value
onDone();
return result;
} catch (Exception ex) {
onDone();
throw ex;
}
}
}
private PartialResultTask<ObservableList<Rectangle>> createListTask() {
PartialResultTask<ObservableList<Rectangle>> task = new PartialResultTask<ObservableList<Rectangle>>() {
@Override
protected ObservableList<Rectangle> calculateResult() throws Exception {updateMessage("Creating Rectangles ...");
ObservableList<Rectangle> results = FXCollections.observableArrayList();
int count = 10;
for (int i = 0; !isCancelled() && i <= count; i++) {
Rectangle r = new Rectangle(10, 10);
r.setX(10 * i);
results.add(r);
updateProgress(i, count);
// Now block the thread for a short time, but be sure
// to check the interrupted exception for cancellation!
try {
Thread.sleep(200);
} catch (InterruptedException interrupted) {
}
}
updateMessage(isCancelled() ? "canceled" : "finished");
return results;
}
};
return task;
}
task.setOnDone(() -> {
table.itemsProperty().unbind();
});
task.stateProperty().addListener((src, ov, nv) -> {
if (Worker.State.SUCCEEDED == nv) {
// this is fine because implementation in TaskCallable first
// updates the value (with the result it got from T call())
// then updates state
LOG.info("succeeded" + task.getValue());
} else if (Worker.State.CANCELLED == nv) {
LOG.info("receiving cancelled " + task.getValue());
}
});