web-dev-qa-db-ja.com

javafx、別のスレッドからUIを更新

Javafxアプリケーションと、javafx.concurrent.Taskを介して実装されるワーカースレッドがあり、長いプロセスを実行します。つまり、一連のファイルを圧縮してアップロードします。
タスクの進行状況をprogressProperty経由で進行状況バーに接続しました。
これに加えて、処理中のアイテムに関する詳細な状態をUIに報告する必要があります。つまり、処理されているファイルの名前とそのサイズ、および単一ファイルプロセスから発生する可能性のあるエラー。
これらの情報を使用してUIを更新することは、ワーカースレッドから実行できません。多くても、同期コレクションに追加できます。
しかし、新しいデータが利用可能であることをUIに通知するイベントが必要です。
javafxには、この問題に対する特定のサポートがありますか?

更新、より良い処方

Platform.runLaterとしてアドホックなクロススレッドメカニズムを設計する代わりに、各プロパティを他のスレッドからリッスンできるようにしています。 Taskが提供するrunningPropertyおよびstatePropertyと同様

30
AgostinoX

エラー処理を自分で処理する必要があると言える限り、同様の問題に直面しています。私の解決策は、メソッド呼び出しを介してUIを更新することです:

何かのようなもの:

  try
  {
    //blah...
  }
  catch (Exception e)
  {
    reportAndLogException(e);
  }
  ...
  public void reportAndLogException(final Throwable t)
  {
    Platform.runLater(new Runnable() {
      @Override public void run() {
        //Update UI here     
      }
    });
  }

基本的に、更新のために手動でUIスレッドに戻すだけです(他のほとんどのフレームワークで行うように)。

34

この答えは、ダニエルの答えと同じ概念を使用しています。

含まれているのは、 Task javadocからの部分結果サンプルのコピーです(Java 8 javadocに現在埋め込まれている構文エラーを修正し、より具体的なGenericタイプを追加するため)。その変更を使用できます。

PartialResultsコレクションに例外を配置します。あなたの場合、タスクから例外のリストを返す必要はありませんが、例外を表示するUIコントロールにそれらを配置することができます(ListViewCellFactory例外表示)。 partialResultsコレクションは、JavaFX UIスレッドで常に更新およびアクセスされるため、同期する必要はありません(更新は、ダニエルのソリューションと同様のPlatform.runLater()呼び出しを介して行われます)。

_public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
    private ReadOnlyObjectWrapper<ObservableList<Rectangle>> partialResults =
            new ReadOnlyObjectWrapper<>(
                    this, 
                    "partialResults",
                    FXCollections.observableArrayList(
                            new ArrayList<>()
                    )
            );

    public final ObservableList<Rectangle> getPartialResults() {
        return partialResults.get();
    }

    public final ReadOnlyObjectProperty<ObservableList<Rectangle>> partialResultsProperty() {
        return partialResults.getReadOnlyProperty();
    }

    @Override
    protected ObservableList<Rectangle> call() throws Exception {
        updateMessage("Creating Rectangles...");
        for (int i = 0; i < 100; i++) {
            if (isCancelled()) break;
            final Rectangle r = new Rectangle(10, 10);
            r.setX(10 * i);
            Platform.runLater(() -> partialResults.get().add(r));
            updateProgress(i, 100);
        }
        return partialResults.get();
    }
}
_

監視可能なプロパティを更新する場合、タスクはまず、FXアプリケーションスレッドで更新が発生しているかどうかを確認します。存在する場合、すぐに更新します。そうでない場合は、Platform.runLater()呼び出しで更新をラップします。 タスクソースコード を参照して、これがどのように行われるかを理解してください。

おそらく、一連の一般的なコンカレントアウェアプロパティを定義することは可能でしょうが、JavaFXはそのような機能をコアに提供していません。確かに、それは必要ありません。 javafx.concurrentパッケージを除き、JavaFXはシングルスレッドUIフレームワークです。

7
jewelsea