On-off success callback for Service

17 hours ago 2
ARTICLE AD BOX

What if I want to execute a one-off on-success callback without permanently adding it to the Service's list of success listeners?

I very much do not want to manually add/remove listeners (as one would occasionally do in Swing).

MRE:

import javafx.application.Application; import javafx.concurrent.Service; import javafx.concurrent.WorkerStateEvent; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.stage.Stage; import java.time.Duration; import java.util.Arrays; import java.util.List; public class FXServiceDemo extends Application { @Override public void start(Stage primaryStage) { primaryStage.setScene(createScene()); primaryStage.setTitle("Service Demo"); primaryStage.show(); } private Scene createScene() { VBox root = createRoot(); Scene scene = new Scene(root); return scene; } private VBox createRoot() { VBox root = new VBox(); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(5)); root.setSpacing(5); root.getChildren().addAll(createButtons()); return root; } private List<Button> createButtons() { Service<Void> service = createService(); Button purpleButton = createPurpleButton(service); Button greenButton = createGreenButton(service); return Arrays.asList(purpleButton, greenButton); } private Service<Void> createService() { return Services.from(this::perform); } private Button createPurpleButton(Service<Void> service) { Button button = new Button(); button.textFillProperty().set(Color.WHITE); button.backgroundProperty().set(new Background(new BackgroundFill(Color.PURPLE, new CornerRadii(10), null))); button.textProperty().set("Purple Button"); button.setOnAction(e -> service.restart()); // this is going to be executed each time the service is successfully finished, even if launched with the GREEN button service.addEventHandler(WorkerStateEvent.WORKER_STATE_SUCCEEDED, e -> System.out.println("Successfully performed from PURPLE button")); return button; } private Button createGreenButton(Service<Void> service) { Button button = new Button(); button.textFillProperty().set(Color.WHITE); button.backgroundProperty().set(new Background(new BackgroundFill(Color.GREEN, new CornerRadii(10), null))); button.textProperty().set("Green Button"); button.setOnAction(e -> service.restart()); // this is going to be executed each time the service is successfully finished, even if launched with the PURPLE button service.addEventHandler(WorkerStateEvent.WORKER_STATE_SUCCEEDED, e -> System.out.println("Successfully performed from GREEN button")); return button; } private void perform() { System.out.println("Doing something..."); sleep(Duration.ofSeconds(1)); System.out.println("Finished the task."); } private static void sleep(Duration duration) { try { Thread.sleep(duration.toMillis()); } catch (InterruptedException e) { throw new RuntimeException(e); } } } import javafx.concurrent.Service; import javafx.concurrent.Task; public class Services { public static Service<Void> from(Runnable task) { return new Service<Void>() { @Override protected Task<Void> createTask() { return new Task<Void>() { @Override protected Void call() { task.run(); return null; } }; } }; } }

No matter whether you click the purple or the green button, the following output is produced:

Doing something... Finished the task. Successfully performed from PURPLE button Successfully performed from GREEN button

Instead it should be:

Doing something... Finished the task. Successfully performed from GREEN button

or

Doing something... Finished the task. Successfully performed from PURPLE button

depending on which button is clicked.

Java 8.

Read Entire Article