在线程中使用Javafx时间线

时间:2018-06-29 21:50:47

标签: java javafx javafx-8 javafx-2

我正在尝试创建一个Internet Checker类,该类将检查与某个URL的连接并相应地更新status属性。为了避免ui冻结,我想使用一个线程,并使用计时器在一定间隔后重新检查。问题是时间轴关键帧中的CHECK方法调用仍从FX线程调用。如何在线程内使用时间线?

代码:

*

2 个答案:

答案 0 :(得分:2)

由于您需要执行与 JavaFX Application Thread 通信的定期 background 任务,因此最好使用ScheduledService。此类使用开发人员可以定义的Executor定期执行{new} Task。请注意,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属性。原因在lastValue的Javadoc中给出:

  

最后一次成功计算的值。在每次迭代中,   与任何其他情况一样,ScheduledService的“值”将重置为null   其他服务。但是,“ lastValue”将设置为最大   最近成功计算的值,即使是在迭代中也是如此。它是   但是,只要您手动调用重置或重新启动,就可以重置。

我建议阅读TaskServiceScheduledService的Javadoc了解更多信息。所有这三个类都实现了javafx.concurrent.Worker接口。

答案 1 :(得分:1)

您只希望在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;
});