Парсит данные с сервера и периодически обновляет GUI - java swing - PullRequest
0 голосов
/ 02 мая 2020

Итак, у меня есть эта программа, которая должна получать данные с сервера, изначально используя objectinputstream. Затем программа должна вставить полученные данные в ArrayList и обновить на экране поле со списком с опциями. Все это прекрасно работает, я просто поместил это здесь для контекста.

Проблема, с которой я столкнулся, заключается в том, что после загрузки JFrame я хочу, чтобы GUI обновлялся каждые 10 секунд новыми данными из сервер, однако это не работает. Я пытался использовать swing.timer, ScheduledExecutorService.scheduleAtFixedRate и TimerTask, однако ни один из них не работал. программа просто зависает (особенно 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); */


}

1 Ответ

1 голос
/ 02 мая 2020

Качели однопоточные. Это означает, что любая длительная или блокирующая операция, выполняемая в контексте потока диспетчеризации событий, приведет к зависанию пользовательского интерфейса.

Недостаточно информации, чтобы быть уверенным, но я подозреваю, что 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 - количество времени, которое потребовалось для выполнения последней задачи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...