首页 > 解决方案 > 为 JavaFX 任务实现暂停和恢复功能

问题描述

我正在为在后台运行的模拟器构建 UI。由于这个 Simulator 可能不会长时间保持,它当然必须在与 JavaFx Thread 不同的线程中。我想在单击相应按钮时启动、暂停、恢复和停止/终止模拟器。推进模拟器的服务类如下所示:

import javafx.concurrent.Service;
import javafx.concurrent.Task;

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

public class SimulatorService extends Service<Void> {

    private Simulator simulator;

    private long cycleLengthMS = 1000;

    private final AtomicBoolean simulatorStopped = new AtomicBoolean(false);


    public SimulatorService(Simulator simulator){
        this.simulator = simulator;
    }


    @Override
    protected Task<Void> createTask() {
        return new Task<>() {
            @Override
            protected Void call() {
                System.out.println("Requested start of Simulator");
                int state;
                do {
                    // advance
                    state = simulator.nextStep();
                    try {
                        TimeUnit.MILLISECONDS.sleep(cycleLengthMS);
                        if(simulatorStopped.get()){
                            super.cancel();
                        }
                    } catch (InterruptedException e) {
                        return null;
                    }

                }
                while (state == 0 && !simulatorStopped.get());

                return null;
            }
        };
    }

    @Override
    public void start(){
        if(getState().equals(State.READY)){
            simulatorStopped.set(false);
            super.start();
        }
        else if(getState().equals(State.CANCELLED)){
            simulatorStopped.set(false);
            super.restart();
        }
    }

    @Override
    public boolean cancel(){
        if(simulatorStopped.get()){
            simulatorStopped.set(true);
            return false;
        } else{
            simulatorStopped.set(true);
            return true; //if value changed
        }
    }
}


如果按下 GUI 上的按钮,模拟器将启动服务:

import javafx.application.Platform;

import java.util.concurrent.atomic.AtomicInteger;

public class Simulator {

    Model model;

    private final SimulatorService simulatorService;


    public Simulator(Model model){
        this.model = model;
        simulatorService = new SimulatorService(this);
    }


    public int nextStep(){
        final AtomicInteger res = new AtomicInteger(0);
        Platform.runLater(new Thread(()-> {
            res.set(model.nextStep());
        }));
        return res.get();
    }


    public boolean stopSimulationService() throws IllegalStateException{
        return simulatorService.cancel();
    }

    public void startSimulationService() throws IllegalStateException{
        simulatorService.start();
    }
}


如果模型中的观察值发生变化,则重新绘制部分窗口:

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;

public class Model {

    private final IntegerProperty observedValue = new SimpleIntegerProperty(0);

    public int nextStep() {
        observedValue.set(observedValue.get() + 1);
        return observedValue.get() > 500000 ? 1 : 0;
    }

    public int getObservedValue() {
        return observedValue.get();
    }

    public IntegerProperty observedValueProperty() {
        return observedValue;
    }

    public void setObservedValue(int observedValue) {
        this.observedValue.set(observedValue);
    }
}


重绘发生在另一个类中:


import javafx.beans.value.ChangeListener;

public class ViewController {

    private View view;

    private Simulator simulator;

    private Model model;

    public ViewController(Simulator simulator) {
        this.simulator = simulator;
        this.view = new View();
        setModel(simulator.model);

        view.nextStep.setOnMouseClicked(click -> {
            simulator.nextStep();

        });

        view.startSim.setOnMouseClicked(click -> {
            simulator.startSimulationService();
        });

        view.stopSim.setOnMouseClicked(click ->{
            simulator.stopSimulationService();
        });
    }

    public View getView() {
        return view;
    }

    private final ChangeListener<Number> observedValueListener = (observableValue, oldInt, newInt) -> {
        view.updateView(newInt.intValue());
    };

    public void setModel(Model m) {
        this.model = m;
        m.observedValueProperty().addListener(observedValueListener);
    }
}


对应视图:

import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Text;

public class View extends BorderPane {

    Button nextStep = new Button("next step");

    Button startSim = new Button("start");

    Button stopSim = new Button("stop");

    GridPane buttons = new GridPane();

    Text num = new Text();

    public View(){
        buttons.add(nextStep,0,0);
        buttons.add(startSim,0,1);
        buttons.add(stopSim,0,2);
        buttons.setAlignment(Pos.BOTTOM_LEFT);
        setCenter(buttons);
        setTop(num);
    }


    public void updateView(int num){
        this.num.setText("" + num);
    }
}


主要的:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);

    }

    @Override
    public void start(Stage stage) throws Exception {
        ViewController c = new ViewController(new Simulator(new Model()));
        Scene scene = new Scene(c.getView(),200,200);
        stage.setScene(scene);
        stage.show();
    }
}


标签: multithreadingjavafxtask

解决方案


推荐阅读