2016-11-18 31 views
0

我想在后台运行一个任务,更新视图中的中间结果。我试图实现MVC JavaFX应用程序。该任务在模型中定义。 我想发送到主要threath部分结果,以便在视图中显示它们。 我使用updateValue()来做到这一点。另外,我在控制器中定义了对象属性和监听器。并发任务更新复杂对象JavaFX - swingworker等效?

我的问题:方法从侦听器更改(),每次在任务中执行updateValue()时都不会被触发。为什么?我如何强迫它做到这一点?

我还没找到很多复杂的例子。

我到目前为止有:

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(); 
} 

我的问题/结论:

  1. 如何强制我执行的所有时间任务updateValue()真正执行侦听器 - 因此在我想绘制数据的位置执行代码。

  2. 为什么它比messageProperty更多的时间触发messageProperty的绑定? - 它应该是相同的次数。

  3. 为什么我发现侦听器的代码在调试模式下比正常执行时更多次触发?

  4. 任何关于此主题(从复杂的角度来看)的良好来源的推荐将是很好的。

  5. 我正在从JavaFX中的东西来取代SwingWorker。

  6. 我真的whant在最后:从任务返回complexObjects的列表,理想updateValue()将发送对象的每一个(部分结果)

我跟随: https://docs.oracle.com/javase/8/javafx/api/javafx/concurrent/Task.html

非常感谢任何contribuction

回答

2

Task只担保将值传递给updateValue或值传递后的Wi将被设置为value属性。这样做是为了通过限制通知监听器的更改次数来提高应用程序线程的性能。

为什么它比valueProperty更多次激发messageProperty的绑定? - 它应该是相同的次数。

如上所述,更新次数根本无法保证。

为什么我发现当调试模式比正常执行时,侦听器的代码被触发多次?

总体调试,使您的程序更小。较小的从Task的线程的更新频率越小Task类更新性质的时间之间的更新的数量和跳过的数量就越少。 (该更新可能执行的每帧或每几帧)。如果你连在任务使用断点/步进,你可能使Task极其缓慢,而应用程序线程以正常速度运行。


这应该是很容易通过使用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(); 
} 
实现你自己的 publish