0

So I am trying to have a method which will have a timer or a thread, so I can auto save every N minutes.(N is the number of minutes) I get N from my XML file, which can be changed anytime. So far this is what I have come up with

private void autoSave(){
    Timer autoSaveTimer = new Timer();

    autoSaveTimer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            System.out.println("Auto saving :D");
            VB.save();
        }
    }, 0, autoSaveTime());
}

autoSaveTime(); just reads the number of minutes from the XMl file and converts it to milliseconds and returns the value. Currently if I change N, it doesn't change.

If there is a different approach I am welling to listen.

PunkKDB
  • 95
  • 1
  • 11
  • Use a [`Timeline`](https://stackoverflow.com/questions/9966136/javafx-periodic-background-task) and change its `rate`; or implement a thread directly with a loop checking for whether it's time to autosave (and sleeping the thread until it is). – James_D Mar 25 '18 at 20:40
  • @James_D if I add a thread within a loop, it won't load up my UI. – PunkKDB Mar 25 '18 at 20:50
  • That would indicate you're doing something wrong. – James_D Mar 25 '18 at 20:50
  • You can't do this directly, Yes you can cancel the tasks of Timer and reschedule them with the desired period. – Bipil Raut Mar 25 '18 at 21:02
  • private void autoSave(){ Timer autoSaveTimer = new Timer(); autoSaveTimer.scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println("Auto saving :D"); VB.save(); autoSaveTimer.cancel();autoSave(); } }, 0, autoSaveTime()); } – Bipil Raut Mar 25 '18 at 21:04
  • You can also consider a [`ScheduledService`](https://docs.oracle.com/javase/9/docs/api/javafx/concurrent/ScheduledService.html), which will (I believe) allow you to change the period, as well as having good semantics for failure and for handling the case where the task takes longer than the time between scheduled tasks. – James_D Mar 25 '18 at 21:07
  • @James_D I tried timeline, I guess I could cancel the timeline, when a new duration is set and re-run it again. But I will check out the ScheduleService and also is there a better way to contact you. I see you do a lot of JavaFX. – PunkKDB Mar 25 '18 at 21:13

1 Answers1

2

Consider using a scheduled service:

public class AutoSaveService implements ScheduledService<Void> {

    @Override
    protected Task<Void> createTask() {
        // retrieve data from UI. This should be done here,
        // as you should access the data on the FX Application Thread
        final MyDataType data = getDataFromUI(); 
        return new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                vb.save(data);
                return null ;
            }
        };
    }
}

You can use this as follows:

AutoSaveService autoSaveService = new AutoSaveService();
autoSaveService.setPeriod(Duration.seconds(5));
autoSaveService.start();

Calls to autoSaveService.setPeriod(...) will be reflected in the time before subsequent tasks are created.

You can also do things like

autoSaveService.setOnFailed(e -> {
    Throwable whatWentWrong = autoSaveService.getException();
    // log exception, warn user, etc...
});

Here's a SSCCE (just prints a message instead of saving data):

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class AutoSaveExample extends Application {

    @Override
    public void start(Stage primaryStage) {

        Spinner<Integer> saveIntervalSpinner = new Spinner<>(1, 60, 1);

        AutoSaveService autoSaveService = new AutoSaveService();
        autoSaveService.periodProperty().bind(Bindings.createObjectBinding(
                () -> Duration.seconds(saveIntervalSpinner.getValue()), 
                saveIntervalSpinner.valueProperty()));
        autoSaveService.start();

        VBox root = new VBox(5, new Label("Save Interval:"), saveIntervalSpinner);
        root.setAlignment(Pos.CENTER_LEFT);
        root.setPadding(new Insets(18));
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static class AutoSaveService extends ScheduledService<Void> {
        @Override
        protected Task<Void> createTask() {
            // retrieve data from UI. This should be done here,
            // as you should access the data on the FX Application Thread
//          final MyDataType data = getDataFromUI(); 
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
//                  vb.save(data);
                    System.out.println("Save at "+System.currentTimeMillis());
                    return null ;
                }
            };
        }
    }


    public static void main(String[] args) {
        launch(args);
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322