Я пишу приложение погоды в javaFx, получая данные с openweather.org.Весь код для получения JSON из openweather работает отлично, также преобразуя данные JSON в объект.Я использовал лямбда-выражение для реализации Runnable
в Platform.runLater();
.Проблема в том, что если я запускаю основной класс, нажимаю кнопку, приложение зависает.Поток для импорта данных работает (проверено 2-мя отпечатками на консоли), а основной поток «пропускает» Platform.runLater();
и печатает что-то на консоли.Я не уверен, что здесь не так.
my Controller class:
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 по назначению.Если нужно больше кода, оставьте комментарий.
@ Редактировать: код для класса, реализующего 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)