从服务器解析数据并定期更新 GUI - java swing
Parse data from server and update GUI periodically - java swing
所以我有这个程序,它最初应该使用对象输入流从服务器接收数据。然后程序应将收到的数据插入 ArrayList 并使用选项更新屏幕上的组合框。所有这些都很好,我只是把它放在这里作为上下文。
我遇到的问题是,一旦加载了 JFrame,我希望 GUI 每 10 秒更新一次来自服务器的新数据,但是它不起作用。我试过使用 swing.timer、ScheduledExecutorService.scheduleAtFixedRate 和 TimerTask,但是 none 似乎有效。程序只是冻结(特别是 gui),控制台中没有显示任何错误,也没有抛出异常。
在下面的代码示例中,我将我当前的尝试以及我以前的一些尝试作为评论包含在内。
代码:
构造函数和设置函数:
public class UserMainGUI extends javax.swing.JFrame {
/**
* Creates new form UserMainGUI
*/
ArrayList<String> data;
JSONParser jsonParser;
ArrayList<String> weatherStationNames;
UserConnection connection;
UpdateDataTimerTask udtt;
Timer timer;
ActionListener taskPerformer;
public UserMainGUI() {
initComponents();
this.data = null;
jsonParser = new JSONParser();
udtt = new UpdateDataTimerTask();
}
public void setupGUI(UserConnection newConnection) //throws InterruptedException
{
connection = newConnection;
if(connection.WeatherStationConnectionCheck())
{
weatherStationOnline.setText("Select a weather station:");
System.out.println("First part working");
data = connection.getWeatherStationData();
if(data != null)
{
parseData();
updateData();
/*taskPerformer = (ActionEvent evt) -> {
data = connection.getWeatherStationData();
System.out.println("updated data: " + data);
parseData();
};
timer = new Timer(10000,taskPerformer);
timer.start(); */
//Thread.sleep(5000);
}
}
else
{
weatherStationComboBox.setVisible(false);
}
}
更新数据函数:
public void updateData()
{
taskPerformer = new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
data = connection.getWeatherStationData();
System.out.println("updated data: " + data);
SwingUtilities.invokeLater(() -> {
parseData();
});
}
};
timer = new Timer(10000,taskPerformer);
timer.start();
/*Thread t = new Thread(new Runnable(){
public void run(){
data = connection.getWeatherStationData();
System.out.println("updated data: " + data);
SwingUtilities.invokeLater(() -> {
parseData();
});
try
{
java.lang.Thread.sleep(10000);
}
catch(Exception ex)
{
//System.err.println("Couldn't update data: " ex)
}
}
});
t.start(); */
/*Runnable retrieveRunnable = new Runnable()
{
@Override
public void run() {
try
{
data = connection.getWeatherStationData();
System.out.println("updated data: " + data);
parseData();
}
catch(Exception ex)
{
System.err.println("Could not update data: " + ex);
}
}
};
ScheduledExecutorService executor = Executors.newScheduledThreadPool(20);
executor.scheduleAtFixedRate(retrieveRunnable, 0, 10, TimeUnit.SECONDS); */
}
Swing 是单线程的。这意味着在事件调度线程的上下文中执行的任何长 运行ning 或阻塞操作都会导致 UI 冻结。
没有足够的信息可以确定,但我怀疑 connection.getWeatherStationData()
是一个阻塞操作,不应在 EDT 内执行。
您可以在 ScheduledExecutorService
上使用 SwingWorker
和 运行,例如...
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel("---");
add(label);
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
LongRunningSufferingTask.secheduleForRun(service, new LongRunningSufferingTask.Tickable() {
private final DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
@Override
public void tick(LocalDateTime dateTime) {
label.setText(formatter.format(dateTime));
}
});
}
}
public static class LongRunningSufferingTask extends SwingWorker<Object, LocalDateTime> {
public interface Tickable {
public void tick(LocalDateTime dateTime);
}
private Tickable tickable;
private ScheduledExecutorService service;
private LongRunningSufferingTask(ScheduledExecutorService service, Tickable tickable) {
this.service = service;
this.tickable = tickable;
}
public static void secheduleForRun(ScheduledExecutorService service, Tickable tickable) {
service.schedule(new LongRunningSufferingTask(service, tickable), 10, TimeUnit.SECONDS);
}
@Override
protected void process(List<LocalDateTime> chunks) {
if (tickable == null) {
return;
}
LocalDateTime last = chunks.get(chunks.size() - 1);
tickable.tick(last);
}
@Override
protected Object doInBackground() throws Exception {
publish(LocalDateTime.now());
// Sleed for a random amount of time to simulate some
// long runing work...
Thread.sleep((int) (Math.random() * 5000));
LongRunningSufferingTask.secheduleForRun(service, tickable);
return "";
}
}
}
此示例假定只有在当前任务完成后才会安排新任务。这防止了两个任务同时执行的可能性,因为最后一个任务必须在安排新任务之前完成。这意味着每个任务将 运行 t + n
在最后一个任务之后,其中 t
是等待的时间量, n
是最后一个任务花费的时间量完成。
所以我有这个程序,它最初应该使用对象输入流从服务器接收数据。然后程序应将收到的数据插入 ArrayList 并使用选项更新屏幕上的组合框。所有这些都很好,我只是把它放在这里作为上下文。
我遇到的问题是,一旦加载了 JFrame,我希望 GUI 每 10 秒更新一次来自服务器的新数据,但是它不起作用。我试过使用 swing.timer、ScheduledExecutorService.scheduleAtFixedRate 和 TimerTask,但是 none 似乎有效。程序只是冻结(特别是 gui),控制台中没有显示任何错误,也没有抛出异常。
在下面的代码示例中,我将我当前的尝试以及我以前的一些尝试作为评论包含在内。
代码:
构造函数和设置函数:
public class UserMainGUI extends javax.swing.JFrame {
/**
* Creates new form UserMainGUI
*/
ArrayList<String> data;
JSONParser jsonParser;
ArrayList<String> weatherStationNames;
UserConnection connection;
UpdateDataTimerTask udtt;
Timer timer;
ActionListener taskPerformer;
public UserMainGUI() {
initComponents();
this.data = null;
jsonParser = new JSONParser();
udtt = new UpdateDataTimerTask();
}
public void setupGUI(UserConnection newConnection) //throws InterruptedException
{
connection = newConnection;
if(connection.WeatherStationConnectionCheck())
{
weatherStationOnline.setText("Select a weather station:");
System.out.println("First part working");
data = connection.getWeatherStationData();
if(data != null)
{
parseData();
updateData();
/*taskPerformer = (ActionEvent evt) -> {
data = connection.getWeatherStationData();
System.out.println("updated data: " + data);
parseData();
};
timer = new Timer(10000,taskPerformer);
timer.start(); */
//Thread.sleep(5000);
}
}
else
{
weatherStationComboBox.setVisible(false);
}
}
更新数据函数:
public void updateData()
{
taskPerformer = new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
data = connection.getWeatherStationData();
System.out.println("updated data: " + data);
SwingUtilities.invokeLater(() -> {
parseData();
});
}
};
timer = new Timer(10000,taskPerformer);
timer.start();
/*Thread t = new Thread(new Runnable(){
public void run(){
data = connection.getWeatherStationData();
System.out.println("updated data: " + data);
SwingUtilities.invokeLater(() -> {
parseData();
});
try
{
java.lang.Thread.sleep(10000);
}
catch(Exception ex)
{
//System.err.println("Couldn't update data: " ex)
}
}
});
t.start(); */
/*Runnable retrieveRunnable = new Runnable()
{
@Override
public void run() {
try
{
data = connection.getWeatherStationData();
System.out.println("updated data: " + data);
parseData();
}
catch(Exception ex)
{
System.err.println("Could not update data: " + ex);
}
}
};
ScheduledExecutorService executor = Executors.newScheduledThreadPool(20);
executor.scheduleAtFixedRate(retrieveRunnable, 0, 10, TimeUnit.SECONDS); */
}
Swing 是单线程的。这意味着在事件调度线程的上下文中执行的任何长 运行ning 或阻塞操作都会导致 UI 冻结。
没有足够的信息可以确定,但我怀疑 connection.getWeatherStationData()
是一个阻塞操作,不应在 EDT 内执行。
您可以在 ScheduledExecutorService
上使用 SwingWorker
和 运行,例如...
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel("---");
add(label);
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
LongRunningSufferingTask.secheduleForRun(service, new LongRunningSufferingTask.Tickable() {
private final DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
@Override
public void tick(LocalDateTime dateTime) {
label.setText(formatter.format(dateTime));
}
});
}
}
public static class LongRunningSufferingTask extends SwingWorker<Object, LocalDateTime> {
public interface Tickable {
public void tick(LocalDateTime dateTime);
}
private Tickable tickable;
private ScheduledExecutorService service;
private LongRunningSufferingTask(ScheduledExecutorService service, Tickable tickable) {
this.service = service;
this.tickable = tickable;
}
public static void secheduleForRun(ScheduledExecutorService service, Tickable tickable) {
service.schedule(new LongRunningSufferingTask(service, tickable), 10, TimeUnit.SECONDS);
}
@Override
protected void process(List<LocalDateTime> chunks) {
if (tickable == null) {
return;
}
LocalDateTime last = chunks.get(chunks.size() - 1);
tickable.tick(last);
}
@Override
protected Object doInBackground() throws Exception {
publish(LocalDateTime.now());
// Sleed for a random amount of time to simulate some
// long runing work...
Thread.sleep((int) (Math.random() * 5000));
LongRunningSufferingTask.secheduleForRun(service, tickable);
return "";
}
}
}
此示例假定只有在当前任务完成后才会安排新任务。这防止了两个任务同时执行的可能性,因为最后一个任务必须在安排新任务之前完成。这意味着每个任务将 运行 t + n
在最后一个任务之后,其中 t
是等待的时间量, n
是最后一个任务花费的时间量完成。