从服务器解析数据并定期更新 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 是最后一个任务花费的时间量完成。