我正在用javaFx编写天气应用程序,以从openweather.org获取数据。从openweather获取JSON的整个代码运行良好,也可以将JSON数据转换为对象。我用lambda表达式来实现Runnable在Platform.runLater();。问题是:如果我运行Main class,请按按钮,该应用程序将挂起。导入数据的线程有效(在控制台上通过2次打印检查),主线程“跳过” Platform.runLater();并在控制台上打印某些内容。我不确定这里出什么问题了。
Runnable
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用自己的线程:
Observable
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)
我认为您误解了Plaftform.runLater的用途。它不是用于运行后台任务,实际上是完全相反的:它是用于将任务委派给JavaFX应用程序线程(或为简单起见而委托给JavaFX应用程序中的“主线程”)。
根据您的描述,我想您想做的是在后台线程中从Web API获取数据,以免阻塞应用程序。为此,您可以使用JavaFX Task,请注意,为了在以后更新GUI,您需要使用Plaftform.runLater