我想在后台运行一个任务,在视图中更新中间结果。我正在尝试实现MVC JavaFX应用程序。任务在模型中定义。 我想发送主要的threath部分结果,以便在View中显示它们。 我使用updateValue()来做到这一点。另外,我在控制器中定义了对象属性和一个监听器。
我的问题:每次在Task中执行updateValue()时,都不会触发来自侦听器的方法changed()。为什么?我怎么强迫它这样做?。
我没有找到太复杂的例子。
到目前为止我所拥有的:
Model.cpp
ComplexObject _complexO;
public Task<ComplexObject> getModelTask() {
return new Task<ComplexObject>() {
@Override
protected ComplexObject call() throws Exception {
int numberOfFiles = 0;
boolean filesToRead = true;
while (filesToRead){
// ....
_complexO = new ComplexObject();
try{
//..
if(f.exists()){
_complexO.initialize();
numberOfScans ++;
}
else{
_complexO.initializeToNull();
}
String stringNumber = Converter.toString(numberOfFiles);
updateMessage(stringNumber);
updateValue(_complexO );
} catch(Exception ex){
ex.printStackTrace();
_complexO = null;
return _complexO;
}
filesToRead = areThereFilesToRead();
}
return _complexO;
}
};
}
Controller.cpp
...
Task< ComplexObject> task = _model.getModelTask();
_AJavaFXTextField.textProperty().bind(task.messageProperty());
_AJavaFXTextField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue observable, String oldValue, String newValue) {
System.out.println("Success with messageProperty!!" + newValue);
}
});
SimpleObjectProperty<ComplexObject> complexObjectProperty = new SimpleObjectProperty<>();
complexObjectProperty.bind(task.valueProperty());
complexObjectProperty.addListener(new ChangeListener<ComplexObject>(){
@Override
public void changed(ObservableValue<? extends ComplexObject> observable, ComplexObject oldValue, ComplexObject newValue) {
if(newValue.data == null ) {
System.out.println("value is new!!! " + scansNumber);
}
else if(newValue.isValid()){
System.out.println("I want to plot newValue data here");
}
}
});
Thread th= new Thread(task);
System.out.println("call TASK");
th.start();
}
我的问题/结论:
如何强制执行我在任务updateValue()中执行的所有时间以真正执行侦听器 - 因此请执行我想要绘制数据的代码。
为什么fireProperty的绑定比valueProperty更多次? - 应该是相同的次数。
为什么我发现调试模式比正常执行时更多次触发侦听器的代码?
任何有关此主题的良好来源的推荐(从复杂的角度来看)都会很棒。
我正在寻找JavaFX中的东西来取代SwingWorker。
最后我真正想要的是:要从任务中返回一个complexObjects列表,理想情况下,updateValue()会逐个发送一个对象(部分结果)
我跟着: https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html
非常感谢任何贡献
答案 0 :(得分:2)
updateValue
仅保证值传递给value
或稍后传递的值将设置为messageProperty
属性。这样做是为了通过限制通知侦听器的更改次数来提高应用程序线程的性能。
为什么
valueProperty
的绑定比Task
更多? - 应该是相同的次数。
如上所述,根本没有关于更新次数的保证。
为什么我发现调试模式比正常执行时更多次触发侦听器的代码?
通常,调试会使您的程序变小。来自Task
的线程的更新频率越小,Task
类更新属性的次数与跳过的次数越小之间的更新次数越少。 (更新可能每帧或每隔几帧执行一次。)如果您甚至在任务中使用断点/步进器,则可能会使应用程序线程以正常速度运行时publish
极慢。
使用List
缓冲更新
public abstract class JavaFXWorker<S, T> extends Task<S> {
private List<T> chunks = new ArrayList<>();
private final Object lock = new Object();
private boolean chunkUpdating = false;
protected final void publish(T... results) {
synchronized (lock) {
chunks.addAll(Arrays.asList(results));
if (!chunkUpdating) {
chunkUpdating = true;
Platform.runLater(() -> {
List<T> cs;
synchronized (lock) {
cs = chunks;
// create new list to not unnecessary lock worker thread
chunks = new ArrayList<>();
chunkUpdating = false;
}
try {
process(cs);
} catch (RuntimeException ex) {
}
});
}
}
}
protected void process(List<T> chunks) {
}
}
@Override
public void start(Stage primaryStage) {
ListView<Integer> lv = new ListView<>();
Button btn = new Button("Run");
btn.setOnAction((ActionEvent event) -> {
JavaFXWorker<Void, Integer> worker = new JavaFXWorker<Void, Integer>() {
@Override
protected Void call() throws Exception {
final int maxCount = 100;
Random random = new Random();
int breakIndex = random.nextInt(maxCount-1)+1;
for (int i = 0; i < breakIndex; i++) {
publish(i);
}
// some break simulating a part long part of the task with no updates
Thread.sleep(3000);
for (int i = breakIndex; i <= maxCount; i++) {
publish(i);
}
return null;
}
@Override
protected void process(List<Integer> chunks) {
lv.getItems().addAll(chunks);
}
};
new Thread(worker).start();
});
Scene scene = new Scene(new VBox(btn, lv));
primaryStage.setScene(scene);
primaryStage.show();
}
使用示例
public function index(){
$data = DB::table('tasks')->paginate(1);
return view('index',['data'=>$data]);
}