【问题标题】:Parse data from server and update GUI periodically - java swing从服务器解析数据并定期更新 GUI - java swing
【发布时间】:2020-05-01 22:00:52
【问题描述】:

所以我有这个程序应该最初使用 objectinputstream 从服务器接收数据。然后程序应将接收到的数据插入 ArrayList 并使用选项更新屏幕上的组合框。所有这些都很好,我只是把它放在这里作为上下文。

我遇到的问题是,一旦 JFrame 已加载,我希望 GUI 每 10 秒使用来自服务器的新数据更新一次,但它不起作用。我尝试过使用 swing.timer、ScheduledExecutorService.scheduleAtFixedRate 和 TimerTask,但似乎都不起作用。程序只是冻结(特别是 gui),控制台中没有显示任何错误,也没有抛出异常。

在下面的代码示例中,我将我当前的尝试以及我之前的一些尝试作为 cmets。

代码:

构造函数和设置函数:

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); */


}

【问题讨论】:

  • 这能回答你的问题吗? Java loading gif freeze while processing data?
  • 有点,我需要定期运行,而不是在单击按钮时运行,但这是朝着正确方向迈出的一步。
  • 您“可以”使用SwingWorker 从服务器请求和解析数据并将其“发布”到 UI。您可以使用ScheduledExecutorService 在规定的延迟后执行它(因为SwingWorker 实现了Runnable 接口) - 这里的重点是 - 如果您在事件调度线程上执行长时间运行或阻塞操作,Swing 是单线程的,它会冻结你的用户界面
  • 更多帮助请发帖minimal reproducible example。避免依赖数据库(硬键一些测试数据)并使用sleep模拟长时间处理

标签: java swing client-server


【解决方案1】:

Swing 是单线程的。这意味着在 Event Dispatching Thread 上下文中执行的任何长时间运行或阻塞操作都会导致 UI 冻结。

没有足够的信息可以确定,但我怀疑connection.getWeatherStationData() 是一个阻塞操作,不应该在 EDT 内执行。

您可以使用SwingWorker 并在ScheduledExecutorService 上运行它,例如...

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 是最后一个任务完成所用的时间。

【讨论】:

    猜你喜欢
    • 2014-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多