Reputation: 131
Can someone please help me out? I have a program which uses JavaFX. Now after pressing a button I want to do n-calculations. Those calculations should be done using threads (parallel) and they should NOT make the main JavaFX app hang / freeze. What I want to do, is to display a loading state while the calcuations are running and if all tasks have finished calculating, I want to continue with the program (remove the loading state and show results).
After reading some stuff about the "Concurrency in JavaFX" I came up with this:
for (int i = 0; i < n; ++i) {
CalcTask task = new CalcTask(i);
task.setOnSucceeded(e -> {
// process is just a static method in which I count
// how many results (i) I already received
// (where n is the required amount). If i == n,
// I know I am done with all the tasks
process(task.getValue());
});
new Thread(task).start();
}
And the CalcTask Class:
public class CalcTask extends Task<Integer> {
protected int id;
public CalcTask (int id) {
this.id = id;
}
@Override
public Integer call() {
return CALCULATION_RESULT;
}
}
Now my question is: This seems a bit "cluncky" to me. Is there any better way for implementing stuff like this in JavaFX? Thanks :)
Upvotes: 1
Views: 1177
Reputation: 924
In my opinion the better way is RxJavaFx
This sample: https://github.com/pkrysztofiak/rxjavafx-demo
RxJavaFx tutorial https://github.com/ReactiveX/RxJavaFX
package com.github.pkrysztofiak.rxjavafx.rxjavafxdemo.concurrency;
import java.util.Random;
import io.reactivex.Observable;
import io.reactivex.rxjavafx.observables.JavaFxObservable;
import io.reactivex.rxjavafx.schedulers.JavaFxScheduler;
import io.reactivex.schedulers.Schedulers;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ParallelTasksApp extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
Button button = new Button("Start");
Label label = new Label();
HBox hBox = new HBox(button, label);
stage.setScene(new Scene(hBox));
stage.show();
JavaFxObservable.actionEventsOf(button)
.flatMap(actionEvent -> Observable.range(1, 4))
.flatMap(i -> Observable.just(i)
.subscribeOn(Schedulers.newThread())
.map(this::runLongProcess))
.observeOn(JavaFxScheduler.platform())
.scan(0, (aggregator, next) -> ++aggregator)
.map(String::valueOf)
.subscribe(label::setText);
}
private int runLongProcess(int i) {
try {
Thread.sleep(new Random().nextInt(10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " i=" + i);
return i;
}
}
Upvotes: 0
Reputation: 13525
Since you extend class Task, you can also overwrite method succeeded()
and remove invocation of task.setOnSucceeded()
on the main thread:
for (int i = 0; i < n; ++i) {
CalcTask task = new CalcTask(i);
new Thread(task).start();
}
public class CalcTask extends Task<Integer> {
protected int id;
public CalcTask (int id) {
this.id = id;
}
public void succeeded() {
process(this.getValue());
}
@Override
public Integer call() {
return CALCULATION_RESULT;
}
}
or even use plain Runnable
instead of Task
:
public class CalcTask implements Runnable {
protected int id;
public CalcTask (int id) {
this.id = id;
}
@Override
public void run() {
CALCULATION_RESULT = calculate();
process(CALCULATION_RESULT);
}
}
Upvotes: 3