多线程应用程序在执行onclickBtn后挂起 [英] Multi thread app hangs after executing onclickBtn
问题描述
我正在javaFx中编写一个天气应用程序,从openweather.org获取数据。从openweather获取JSON的整个代码工作正常,将JSON数据转换为对象。我使用lambda表达式在 Platform.runLater();
中实现 Runnable
。问题是:如果我运行Main类,按下按钮,应用程序挂起。用于导入数据的线程(通过控制台上的2个打印检查)和主线程跳过 Platform.runLater();
并在控制台上打印一些内容。我不确定这里有什么问题。
I'm writing a weather app in javaFx getting data from openweather.org. The whole code for getting JSON from openweather works fine, converting JSON data to an object too. I used lambda expression to implement Runnable
in Platform.runLater();
. The problem is: if I run the Main class, press the button, the app hangs. The thread for importing data works (checked by 2 prints on console) and the main thread "skips" Platform.runLater();
and prints something on the console. I'm not sure what is wrong here.
我的Controller类:
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转换的东西按预期工作。如果需要更多代码,请发表评论。
Classes used for generating plot data, converting things from JSON work as intended. If more code is needed, leave a comment.
@Edit:实现 Observable
的类的代码, Runnable
有自己的主题:
@ The code for a class implementing Observable
, Runnable
with own thread:
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'方法的修改结合上面的类:
Modification of 'onclickBtn' method combined with the class above:
@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应用程序中使用主线程)。
I think you misunderstood what Plaftform.runLater is intended for. It's not for running background tasks, in fact it's the complete opposite: it's for delegating tasks to the JavaFX application thread (Or "main thread" in a JavaFX app for simplicity).
根据您的描述,我猜你想要做的是在后台线程中从Web API获取数据,这样它就不会阻止应用程序。要执行此操作,您可以使用 JavaFX任务,注意为了在以后更新GUI,您需要使用 Plaftform.runLater
Based on your description I would guess that what you want to do is to fetch the data from the web API in a background thread so that it doesn't block the application. To do this you can use JavaFX Task, note that in order to update the GUI afterwards you need to use Plaftform.runLater
这篇关于多线程应用程序在执行onclickBtn后挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!