0

So I have this program that should receive data from a server initially using an objectinputstream. The program should then insert the data received into a ArrayList and update a combo box on the screen with options. All of this works fine I just put it in here for context.

The problem I'm having is that once the the JFrame has loaded I want the GUI to be updated every 10 seconds with new data from the server however it doesn't work. I've tried using swing.timer, ScheduledExecutorService.scheduleAtFixedRate and TimerTask however none seemed to work. the program just freezes (the gui specifically) and no errors are being shown in the console and no exceptions are thrown.

In the code example below I have included my current attempt as well as some of my previous ones as comments.

Code:

constructor and set up function:

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);
    }
}

update data function:

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


}
TavianT
  • 55
  • 6
  • Does this answer your question? [Java loading gif freeze while processing data?](https://stackoverflow.com/questions/52079885/java-loading-gif-freeze-while-processing-data) – George Z. May 01 '20 at 22:21
  • kind of, I need to be run periodically and not when a button is clicked however this is a step in the right direction. – TavianT May 01 '20 at 23:15
  • You "could" use a `SwingWorker` to request and parse the data from the server and "publish" it to the UI. You could use a `ScheduledExecutorService` to execute it after the prescribed delay (as `SwingWorker` implements the `Runnable` interface) - The important point here is - Swing is single threaded if you perform long running or block operations on the Event Dispatching Thread, it will freeze your UI – MadProgrammer May 02 '20 at 00:07
  • For more help post [mre]. Avoid dependency on data-base (hard key some test data) and simulate long processing by using `sleep` – c0der May 02 '20 at 06:05

1 Answers1

1

Swing is single threaded. This means that any long running or blocking operation executed within the context of the Event Dispatching Thread will cause the UI to freeze.

There's not enough information to be sure, but I suspect that connection.getWeatherStationData() is a blocking operation and shouldn't be executed within the EDT.

You could make use of a SwingWorker and run it on a ScheduledExecutorService, for example...

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 "";
        }

    }
}

This example assumes that a new task will scheduled only after the current task has completed. This prevents the possibility for two tasks to execute at the same time, as the last task will have had to complete before a new task is scheduled. This means that each task will run t + n after the last task, where t is the amount of time to wait and n is the amount of time the last task took to complete.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366