所以我有这样一个程序,它应该从服务器接收数据,最初使用一个objectinputstream,然后程序应该将接收到的数据插入到一个ArrayList中,并在屏幕上更新一个带有选项的组合框。然后程序应该将接收到的数据插入到一个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); */
}
Swing是单线程的。 这意味着,在事件派遣线程的上下文中执行的任何长期运行或阻塞操作都会导致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
是最后一项任务完成的时间。