2017-11-21 8 views
2

私はjavafx.concurrent.Taskを使用して、JavaFXアプリケーションのバックグラウンド作業を行っています。進捗情報を呼び出し可能からタスクに伝播する

このようなタスクを単体テストするとき、私はたいていtask.call()を呼び出します。 task.run()とは対照的に、これはタスクの状態を変更しません(here,も参照してください)。削除の取り消しはに投票してください。 update..()メソッドを呼び出さないと、JavaFXプラットフォームが実行されていなくてもtask.call()を呼び出すことができます。

私が実行しているJavaFXの環境から独立しように、このアプローチをリファクタリングし、Callable独立したJavaFXへcall()方法の実装を移動したいと思います。

この独立したクラスをテストする方が簡単で、この呼び出し可能な関数はタスク内から簡単に使用できます。

質問はどのようにして呼び出し元の進行状況を更新し、呼び出し元のタスクにこの進行状況情報を伝播できますか?

Taskは保護update...()方法を介して進捗更新を行い、そのための専用オブジェクトを使用していない(例えば等org.eclipse.core.runtime.IProgressMonitor)簡単にこの機能を委任することになります。

答えて

3

まず、javafx.concurrentパッケージはFX Toolkitの実行に依存しますが、JavaFX Propertiesクラスにはそのような要件はありません。したがって、単一のスレッドからのみJavaFXプロパティにアクセスする限り、FX Toolkitの実行を必要とせずに、使用したいクラスでJavaFXプロパティを使用することは完全に有効です。

だから例えば、あなたが行うことができます:

public class MyProcess implements Callable<SomeData> { 

    private final ReadOnlyDoubleWrapper progress = new ReadOnlyDoubleWrapper(); 

    public ReadOnlyDoubleProperty progressProperty() { 
     return progress.getReadOnlyProperty() ; 
    } 

    public final double getProgress() { 
     return progressProperty().get(); 
    } 

    @Override 
    public SomeData call() { 
     final int numSteps = ... ; 
     SomeData data = new SomeData(); 
     for (int i = 0 ; i < numSteps ; i++) { 
      progress.set(1.0*i/numSteps); 
      data.update(i); 
     } 
     return data ; 
    } 
} 

このクラスは罰金実行され、FXツールキットの実行には依存しません。次のようにして、タスクでそれをラップすることができます:

public class MyProcessTask extends Task<SomeData> { 

    @Override 
    protected SomeData call() throws Exception { 
     MyProcess process = new MyProcess(); 
     process.progressProperty().addListener((obs, oldProgress, newProgress) -> 
      updateProgress(newProgress, 1.0)); 
     return process.call(); 
    } 
} 

あなたCallable実装は(だけでなく、実行されているツールキットを必要としないという意味で)APIレベルでのJavaFXに依存しないようにしたい場合は、それがありますjava.util.function APIを使用して適切なコールバックを簡単に実装できます。たとえば、

public class MyProcess implements Callable<SomeData> { 

    private DoubleConsumer progressUpdate = d -> {} ; // no-op 
    private double progress ; 

    public void setProgressUpdate(DoubleConsumer progressUpdate) { 
     this.progressUpdate = progressUpdate ; 
    } 

    public final double getProgress() { 
     return progress; 
    } 

    private void setProgress(double progress) { 
     this.progress = progress ; 
     progressUpdate.accept(progress); 
    } 

    @Override 
    public SomeData call() { 
     final int numSteps = ... ; 
     SomeData data = new SomeData(); 
     for (int i = 0 ; i < numSteps ; i++) { 
      setProgress(1.0*i/numSteps) ; 
      data.update(i); 
     } 
     return data ; 
    } 
} 

public class MyProcessTask extends Task<SomeData> { 

    @Override 
    protected SomeData call() throws Exception { 
     MyProcess process = new MyProcess(); 
     process.setProgressUpdate(p -> updateProgress(p, 1.0)); 
     return process.call(); 
    } 
} 
+0

パーフェクトとして上記をリファクタリングできます!私は 'updateMessage()'にも同様のアプローチを使用できます。しかし、取り消しはどうですか?そのために「Consumer 」を使用するのは意味がありますか? – kerner1000

+1

'Callable'は' Task'sと同じ意味でキャンセルできません。 「取り消されました」は、FXアプリケーションスレッド上で更新される(タスクランクに対応する状態の一部です(したがって、 'RUNNING'、' SUCCEEDED'などと同じカテゴリに入ります)。 –

+0

したがって、ユーザーによるキャンセル要求を聞くにはどうすればよいでしょうか? 'Thread.isInterrupted()'をチェックしますか? – kerner1000

関連する問題