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 buttonInstead it should be:
Doing something... Finished the task. Successfully performed from GREEN buttonor
Doing something... Finished the task. Successfully performed from PURPLE buttondepending on which button is clicked.
Java 8.
