Javafx中的计时器创建FX线程空指针和越界异常 [英] Timer in Javafx Creating FX Thread null pointers and out of bounds exceptions

查看:645
本文介绍了Javafx中的计时器创建FX线程空指针和越界异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在Javafx中使用GUI和Timer类创建一个简单的计时器。当按下开始按钮时,它应该从输入时间开始倒计时并在达到0时停止。

I was trying to make a simple timer in Javafx with a GUI and Timer class. When the start button is pressed, it is supposed to count down from the inputed time and stop when it reaches 0.

我有一个在TextField中更新的剩余时间(以毫秒为单位)在GUI上,它只运行一个随机的毫秒数,通常在100-200之间,然后冻结并抛出非常多的异常。

I have the remaining time in milliseconds updating in a TextField on the GUI but it only runs for a random number of milliseconds usually between 100-200 and then it freezes up and throws a VERY large number of exceptions.

我试图查明错误来自何处,并发现还存在并发修改异常。

I tried to pinpoint where the error was coming from, and found there was also a concurrent modification exception as well.

这是我的Timer类:

Here is my Timer class:

import java.sql.Time;

/**
 * Created by Josh on 19/8/2015.
 */
public class Timer {

    private long endTimeMillis;

    public Timer(long hours, long minutes, long seconds){

        long currentTime = System.currentTimeMillis();

        endTimeMillis = ((seconds*1000 + minutes*1000*60 + hours*1000*60*60) + currentTime);

    }

    public boolean isFinished(){

        long currentTime = System.currentTimeMillis();

        if(endTimeMillis - currentTime <= 0){
            return true;
        }else{
            return false;
        }

    }

    public long getRemainingTimeMillis(){

        long currentTime = System.currentTimeMillis();

        return endTimeMillis - currentTime;

    }

}

这是GUI

 import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

/**
 * Created by Josh on 19/8/2015.
 */
public class GUI extends Application {

    private BorderPane root;

    Label hoursLabel, minutesLabel, secondsLabel;
    TextField hoursTF, minutesTF, secondsTF;

    Button startButton;

    Label remainingTimeLabel;
    TextField remainingTimeTF;

    long remainingTime;

    @Override
    public void start(Stage primaryStage) throws Exception {

        root = new BorderPane();
        primaryStage.setScene(new Scene(root, 1300, 125));
        primaryStage.show();
        primaryStage.setResizable(true);
        primaryStage.setTitle("Timer");
        primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent event) {
                event.consume();
                System.exit(0);
            }
        });

        FlowPane center = new FlowPane();
        hoursLabel = new Label("     Hours: ");
        hoursTF = new TextField();
        minutesLabel = new Label("     Minutes: ");
        minutesTF = new TextField();
        secondsLabel = new Label("     Seconds: ");
        secondsTF = new TextField();
        center.getChildren().addAll(hoursLabel, hoursTF, minutesLabel, minutesTF, secondsLabel, secondsTF);
        root.setCenter(center);

        FlowPane bottom = new FlowPane();
        bottom.setAlignment(Pos.CENTER);
        startButton = new Button("Start");
        startButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                start();
            }
        });
        remainingTimeLabel = new Label("     Time Remaining: ");
        remainingTimeTF = new TextField();
        bottom.getChildren().addAll(startButton, remainingTimeLabel, remainingTimeTF);
        root.setBottom(bottom);


    }



    public void start(){

        Task timer = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                long hours = 0, minutes = 0, seconds = 0;

                try{
                    if(hoursTF.getText() == null || hoursTF.getText().equals("")){
                        hours = 0;
                    }else{
                        hours = Long.parseLong(hoursTF.getText());
                    }
                    if(minutesTF.getText() == null || minutesTF.getText().equals("")){
                        minutes = 0;
                    }else{
                        minutes = Long.parseLong(minutesTF.getText());
                    }
                    if(secondsTF.getText() == null || secondsTF.getText().equals("")){
                        seconds = 0;
                    }else{
                        seconds = Long.parseLong(secondsTF.getText());
                    }
                }catch(NumberFormatException e){
                    System.out.println("Error");

                }

                Timer timer = new Timer(hours, minutes, seconds);

                while(!timer.isFinished()){
                    remainingTimeTF.setText(Long.toString(timer.getRemainingTimeMillis()));
                }


                return null;
            }
        };
        new Thread(timer).start();

    }

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

任何帮助都将不胜感激!

any help would be appreciated!

新堆栈跟踪:

    x.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
    at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
    at javafx.scene.control.Labeled.setText(Labeled.java:145)
    at GUI$3.call(GUI.java:109)
    at GUI$3.call(GUI.java:80)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1423)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.lang.Thread.run(Thread.java:745)
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:438)
    at javafx.scene.Parent$2.onProposedChange(Parent.java:364)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:575)
    at com.sun.javafx.scene.control.skin.LabeledSkinBase.handleControlPropertyChanged(LabeledSkinBase.java:204)
    at com.sun.javafx.scene.control.skin.LabelSkin.handleControlPropertyChanged(LabelSkin.java:49)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase.lambda$registerChangeListener$61(BehaviorSkinBase.java:197)
    at com.sun.javafx.scene.control.skin.BehaviorSkinBase$$Lambda$102/1442917786.call(Unknown Source)
    at com.sun.javafx.scene.control.MultiplePropertyChangeListenerHandler$1.changed(MultiplePropertyChangeListenerHandler.java:55)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:103)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:144)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:49)
    at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
    at javafx.scene.control.Labeled.setText(Labeled.java:145)
    at GUI$3.call(GUI.java:109)
    at GUI$3.call(GUI.java:80)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1423)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.lang.Thread.run(Thread.java:745)


推荐答案

您违反了关于不在后台线程上访问实时场景图中节点状态的规则。大多数JavaFX控件实际上会检查这个并在执行此操作时抛出 IllegalStateException :显然 TextInputControl.setText(...)没有(大概是出于性能原因)。

You are violating the rule about not accessing the state of a node in a live scene graph on a background thread. Most JavaFX controls will actually check for this and throw an IllegalStateException when you do this: apparently TextInputControl.setText(...) doesn't (presumably for reasons of performance).

最简单的解决方法是更新任务的消息属性:

The simplest fix is to update the message property of the task:

            while(!timer.isFinished()){
              updateMessage(Long.toString(timer.getRemainingTimeMillis()));
              // remainingTimeTF.setText(Long.toString(timer.getRemainingTimeMillis()));
            }

(这会导致在FX应用程序线程上更新消息属性),然后将文本字段的text属性绑定到它:

(this causes an update to the message property to occur on the FX Application Thread), and then bind the text field's text property to it:

    remainingTimeTF.textProperty().bind(timer.messageProperty());
    new Thread(timer).start();

但是,您的代码有很多违反良好做法的事情。例如,您应该真正计算任务之外的总时间(即在FX应用程序线程上)并将其存储在 final 变量中;繁忙的while循环非常糟糕(虽然 updateMessage(...)将解决与此相关的大部分问题。)

However, there are a lot of things about your code that violate good practice. Fo example, you should really compute the total time outside of the task (i.e. on the FX Application Thread) and store it in a final variable; and the busy while loop is pretty horrible (though updateMessage(...) will fix most of the issues associated with that for you).

我建议您阅读JavaFX中的多线程。从 任务开始 API文档。也许这个问题会有所帮助:使用线程发出数据库请求

I recommend you read up on multithreading in JavaFX. Start with the Task API docs. Maybe this question will help: Using threads to make database requests

这篇关于Javafx中的计时器创建FX线程空指针和越界异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆