Использование Javafx TimeLine в потоке - PullRequest
0 голосов
/ 30 июня 2018

Я пытаюсь создать класс интернет-проверки, который будет проверять соединение с определенным URL-адресом и соответствующим образом обновлять свойство status. Чтобы избежать заморозки, я хочу использовать поток и таймер для повторной проверки через определенный интервал. Проблема в том, что вызов метода CHECK в ключевом кадре временной шкалы все еще вызывается из потока FX. Как я могу использовать временную шкалу внутри потока?

КОД:

public class InternetChecker {
    private String baseUrl;
    /***Properties***/
    private ObjectProperty<Status> status = new SimpleObjectProperty<>(Status.ACTIVE);

    /****************************************************************
     **********                  CONSTRUCTORS            ************
     ****************************************************************/
    public InternetChecker(String baseUrl) {
        this(baseUrl, 1000);
    }

    public InternetChecker(String baseUrl, int millisCheckInterval) {
        this.baseUrl = baseUrl;
        new Thread(() -> {
            Timeline timelineCheck = new Timeline();
            timelineCheck.getKeyFrames().add(
                    new KeyFrame(Duration.millis(millisCheckInterval), e -> {
                        check();
                    }));
            timelineCheck.setCycleCount(Animation.INDEFINITE);
            timelineCheck.play();
        }).start();
    }

    /*******************************
     * Will check if there is an internet connection present
     * and update the status accordingly
     *******************************/
    public void check() {
        // Check if base internet connection
        // is working, if it is we continue
        // to see if domain connection is working
        try {
            if ("127.0.0.1".equals(InetAddress.getLocalHost().getHostAddress())) {
                setStatus(Status.INTERNET_DISCONNECTED);
                return;
            }
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
        // Check if base domain connection is working
        try {
            final URL url = new URL(baseUrl);
            final URLConnection conn = url.openConnection();
            conn.connect();
            conn.getInputStream().close();
            setStatus(Status.ACTIVE);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            setStatus(Status.BASE_URL_UNREACHABLE);
        }
    }

    /****************************************************************
     **********                  ACCESSORS               ************
     ****************************************************************/

    public Status getStatus() {
        return status.get();
    }

    public ObjectProperty<Status> statusProperty() {
        return status;
    }

    private void setStatus(Status status) {
        this.status.set(status);
    }

    /*******************************
     *  ACTIVE (Base url reachable)
     *  BASE_URL_UNREACHABLE (Internet available, but base url is unreachable)
     *  INTERNET_DISCONNECTED (Internet is not available)
     ********************************/
    public enum Status {
        ACTIVE,
        BASE_URL_UNREACHABLE,
        INTERNET_DISCONNECTED;
    }
}

Ответы [ 2 ]

0 голосов
/ 30 июня 2018

Вы хотите, чтобы только один оператор выполнялся в потоке приложения JavaFX, и это status.set(status);. Поскольку вы планируете выполнить этот оператор с некоторой задержкой между ними, вы можете просто использовать Platform.runLater для этого.

Что касается повторного выполнения чека: ScheduledExecutorService предназначен для этой цели.

public class InternetChecker implements Runnable {
    private final String baseUrl;
    /***Properties***/

    // use readonly wrapper here to restrict outside access to the property
    private final ReadOnlyObjectWrapper<Status> status = new ReadOnlyObjectWrapper<>(Status.ACTIVE);

    /****************************************************************
     **********                  CONSTRUCTORS            ************
     ****************************************************************/
    public InternetChecker(String baseUrl) {
        this.baseUrl = baseUrl;
    }

    /*******************************
     * Will check if there is an internet connection present
     * and update the status accordingly
     *******************************/
    @Override
    public void run() {
        // Check if base internet connection
        // is working, if it is we continue
        // to see if domain connection is working
        try {
            if ("127.0.0.1".equals(InetAddress.getLocalHost().getHostAddress())) {
                setStatus(Status.INTERNET_DISCONNECTED);
                return;
            }
        } catch (UnknownHostException e) {
            throw new RuntimeException(e);
        }
        // Check if base domain connection is working
        try {
            final URL url = new URL(baseUrl);
            final URLConnection conn = url.openConnection();
            conn.connect();
            conn.getInputStream().close();
            setStatus(Status.ACTIVE);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            setStatus(Status.BASE_URL_UNREACHABLE);
        }
    }

    /****************************************************************
     **********                  ACCESSORS               ************
     ****************************************************************/

    public Status getStatus() {
        return status.get();
    }

    public ReadOnlyObjectProperty<Status> statusProperty() {
        return status.getReadOnlyProperty​();
    }

    private void setStatus(final Status status) {
        Platform.runLater(() -> this.status.set(status));
    }

    /*******************************
     *  ACTIVE (Base url reachable)
     *  BASE_URL_UNREACHABLE (Internet available, but base url is unreachable)
     *  INTERNET_DISCONNECTED (Internet is not available)
     ********************************/
    public enum Status {
        ACTIVE,
        BASE_URL_UNREACHABLE,
        INTERNET_DISCONNECTED;
    }
}
InternetChecker checker = new InternetChecker(url);
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor​();

// use delay here to avoid race condition
executorService.scheduleAtFixedDelay(checker, 0, millisCheckInterval, TimeUnit.MILLISECONDS);

Обратите внимание, что вам нужно завершить работу службы "вручную" или использовать ThreadFactory возвращающие потоки демона:

ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor​(r -> {
    Thread t = new Thread(r);
    t.setDaemon(true);
    return t;
});
0 голосов
/ 30 июня 2018

Поскольку вам необходимо выполнять периодическую задачу background , которая связывается с Потоком приложений JavaFX , было бы лучше использовать ScheduledService. Этот класс периодически выполняет (новый) Task, используя Executor, который может быть определен разработчиком. Обратите внимание, что ScheduledService расширяет javafx.concurrent.Service.

Вот скелетный пример того, что вам нужно сделать, чтобы реализовать это:

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;

public class ConnectionStatusService extends ScheduledService<Status> {

    // Property allows you to change the "baseUrl" between executions
    private final StringProperty baseUrl = new SimpleStringProperty(this, "baseUrl");

    // property getter and setters omitted...

    @Override
    protected Task<Status> createTask() {
        // creates a new Task and gives the current "baseUrl"
        // as an argument. This is called every cycle
        return new ConnectionStatusTask(getBaseUrl());
    }

    private static class ConnectionStatusTask extends Task<Status> {

        // A Task is a one-shot thing and its initial state should be
        // immutable (or at least encapsulated from external modification).
        private final String baseUrl;

        private ConnectionStatusTask(String baseUrl) {
            this.baseUrl = baseUrl;
        }

        @Override
        protected Status call() throws Exception {
            // Do what you need to determine connection status
            return computedStatus;
        }
    }

}

Тогда вы будете слушать / связывать свойство lastValue.

public void initService() {
    ConnectionStatusService service = new ConnectionStatusService();
    service.setBaseUrl(/* your URL */);
    service.setPeriod(Duration.seconds(1)); // run every 1 seconds
    service.lastValueProperty().addListener(/* your listener */); // or bind to this property

    // you may also want to add EventHandlers/Listeners to handle when the
    // service fails and such.
    service.start();
}

Важно, что вы соблюдаете свойство lastValue, а не свойство value. Причина указана в Javadoc lastValue:

Последнее успешно вычисленное значение. Во время каждой итерации «значение» ScheduledService будет сброшено на ноль, как и в любом другой сервис. Однако «lastValue» будет установлено на недавно успешно вычисленное значение, даже между итерациями. это сбрасывайте, однако, всякий раз, когда вы вручную вызываете сброс или перезапуск.

Я рекомендую прочитать Javadoc Task, Service и ScheduledService для получения дополнительной информации. Все три из этих классов реализуют интерфейс javafx.concurrent.Worker.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...