我正在用javaFx编写天气应用程序,以从openweather.org获取数据。从openweather获取JSON的整个代码运行良好,也可以将JSON数据转换为对象。我使用lambda表达式在Runnable
中实现了Platform.runLater();
。问题是:如果我运行Main class,请按按钮,该应用程序将挂起。导入数据的线程有效(在控制台上通过2次打印检查),主线程“跳过” Platform.runLater();
,并在控制台上打印内容。我不确定这是怎么回事。
我的控制器类:
package sample;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import java.time.LocalTime;
import java.util.ArrayList;
public class Controller implements Observable{
private static final WeatherStation WEATHER_STATION = new WeatherStation();
protected volatile boolean isRunning = false;
private String response;
private static final int _5MINUTES = 1000*60*5;
private volatile ArrayList<Observer> observers = new ArrayList<>();
@FXML
private TextField cityTextfield;
@FXML
private CategoryAxis xAxis;
@FXML
private NumberAxis yAxis;
@FXML
private Button btn;
@FXML
private LineChart<String, Number> plot;
@FXML
void onclickBtn(ActionEvent event) throws InterruptedException {
WeatherUpdater weatherUpdater = new WeatherUpdater(plot);
setSettings();
WeatherObserver wroclaw = new WeatherObserver();
weatherUpdater.addObserver(wroclaw);
Platform.runLater(()->{
isRunning = true;
while(isRunning) try {
addObserver(wroclaw);
PlotDataUpdater<String, Number> dataUpdater = new PlotDataUpdater<>();
WEATHER_STATION.sendQuery();
response = WEATHER_STATION.getCurrentResponse();
updateObservers();
WeatherConditions weatherConditions = observers.get(0).getWeatherConditions();
LocalTime currentTime = LocalTime.of(LocalTime.now().getHour(), LocalTime.now().getMinute());
dataUpdater.updateSeries(currentTime.toString(), weatherConditions.getMainTemp());
dataUpdater.updatePlot(plot);
Thread.currentThread().sleep(_5MINUTES);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.out.println("Thread interrupted");
}
});
System.out.println(".................................");
}
@Override
public void addObserver(Observer observer) {
if(!observers.contains(observer)) observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if(observers.contains(observer)) observers.remove(observer);
}
@Override
public void updateObservers() {
for(Observer o : observers){
o.updateWeatherInfo(response);
}
}
private void setSettings(){
plot.getData().clear();
xAxis.setAutoRanging(true);
yAxis.setAutoRanging(true);
plot.setAnimated(false);
}
}
用于生成绘图数据,从JSON转换事物的类按预期工作。如果需要更多代码,请发表评论。
@Edit:使用自己的线程实现Observable
,Runnable
的类的代码:
package sample;
import javafx.scene.chart.XYChart;
import java.time.LocalTime;
import java.util.ArrayList;
public class WeatherUpdater implements Runnable, Observable{
private Thread updater;
private static final WeatherStation WEATHER_STATION = new WeatherStation();
protected volatile boolean isRunning = false;
private String response;
private static final int _5MINUTES = 1000*60*5;
private volatile ArrayList<Observer> observers = new ArrayList<>();
private XYChart<String, Number> plot;
public WeatherUpdater(XYChart<String, Number> plot) {
this.plot = plot;
}
public WeatherUpdater() {
}
public void start(){
updater = new Thread(this,"Weather updater");
updater.start();
}
public void interrupt(){
isRunning = false;
updater.interrupt();
}
public XYChart<String, Number> getPlot() {
return plot;
}
@Override
public void run() {
isRunning = true;
while(isRunning){
try{
PlotDataUpdater<String, Number> dataUpdater = new PlotDataUpdater<>();
WEATHER_STATION.sendQuery();
response = WEATHER_STATION.getCurrentResponse();
updateObservers();
WeatherConditions weatherConditions = observers.get(0).getWeatherConditions();
LocalTime currentTime = LocalTime.of(LocalTime.now().getHour(), LocalTime.now().getMinute());
dataUpdater.updateSeries(currentTime.toString(), weatherConditions.getMainTemp());
dataUpdater.updatePlot(plot);
Thread.sleep(_5MINUTES);
} catch (InterruptedException e) {
updater.interrupt();
System.out.println("Thread interrupted" );
}
}
}
@Override
public void addObserver(Observer observer) {
if(!observers.contains(observer)) observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
if(observers.contains(observer)) observers.remove(observer);
}
@Override
public void updateObservers() {
for(Observer o : observers){
o.updateWeatherInfo(response);
}
}
}
结合上面的类对'onclickBtn'方法的修改:
@FXML
void onclickBtn(ActionEvent event) throws InterruptedException {
WeatherUpdater weatherUpdater = new WeatherUpdater(plot);
setSettings();
WeatherObserver wroclaw = new WeatherObserver();
weatherUpdater.addObserver(wroclaw);
weatherUpdater.start();
System.out.println(".................................");
}
控制台输出:
.................................
Server status: 200
Exception in thread "Weather updater" java.lang.IllegalStateException: Not on FX application thread; currentThread = Weather updater
at javafx.graphics/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:291)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:424)
at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:471)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:206)
at javafx.controls/javafx.scene.chart.LineChart.seriesAdded(LineChart.java:405)
at javafx.controls/javafx.scene.chart.XYChart.lambda$new$1(XYChart.java:160)
at javafx.base/com.sun.javafx.collections.ListListenerHelper$SingleChange.fireValueChangedEvent(ListListenerHelper.java:164)
at javafx.base/com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
at javafx.base/javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.base/javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.base/javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.base/javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.base/javafx.collections.ModifiableObservableListBase.setAll(ModifiableObservableListBase.java:90)
at javafx.base/javafx.collections.ObservableListBase.setAll(ObservableListBase.java:251)
at sample.PlotDataUpdater.updatePlot(PlotDataUpdater.java:36)
at sample.WeatherUpdater.run(WeatherUpdater.java:56)
at java.base/java.lang.Thread.run(Thread.java:834)
答案 0 :(得分:3)
我认为您误解了Plaftform.runLater的用途。它不是用于运行后台任务,实际上是完全相反的:它是用于将任务委派给JavaFX应用程序线程(或为简单起见而委托给JavaFX应用程序中的“主线程”)。
根据您的描述,我想您想做的是在后台线程中从Web API获取数据,以免阻塞应用程序。为此,您可以使用JavaFX Task,请注意,之后要更新GUI,您需要使用Plaftform.runLater