Reputation: 31
As per my previous question What's the best way to have a dynamic JTable whose data updates itself with changes in database table? , I have used the following code for an automatically updating jList
DefaultListModel x = new DefaultListModel();
jList1.setModel(x);
int initialDelay = 0;
int period = 5000;
Timer timer = new Timer();
TimerTask task = new TimerTask() {
public void run() {
x.clear();
try {
conn = Database.Connect("patient.db");
sql = "SELECT * FROM PATIENT_APPOINTMENTS";
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
while(rs.next()) {
x.addElement(rs.getString("patientid"));
}
}
catch ( Exception e) {
JOptionPane.showMessageDialog(null,e,"Error",JOptionPane.ERROR_MESSAGE);
}
}
};
timer.scheduleAtFixedRate(task,initialDelay,period);
But I have read about SwingWorker and it seems to be the right way to do it .A normal implementation of swing worker has 3 protected methods of doInBackground , publish and done . I also tried another version with SwingWorker which is
DefaultListModel x = new DefaultListModel();
jList1.setModel(x);
int initialDelay = 0;
int period = 5000;
Timer timer = new Timer();
TimerTask task = new TimerTask() {
public void run() {
x.clear();
try {
SwingWorker<ArrayList<String>, Void> worker = new SwingWorker<ArrayList<String>, Void>() {
protected ArrayList<String> doInBackground() throws Exception {
ArrayList<String> patientid = new ArrayList<String>();
conn = Database.Connect("patient.db");
sql = "SELECT * FROM PATIENT_APPOINTMENTS";
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
while (rs.next()) {
patientid.add(rs.getString("patientid"));
}
return patientid;
}
protected void done() {
ArrayList<String> id = new ArrayList<String>();
try {
id = get();
for (int i = 0; i < id.size(); i++) {
x.addElement(id.get(i));
}
} catch (InterruptedException e) {
JOptionPane.showMessageDialog(null, e, "Error", JOptionPane.ERROR_MESSAGE);
} catch (ExecutionException e) {
JOptionPane.showMessageDialog(null, e, "Error", JOptionPane.ERROR_MESSAGE);
}
}
};
worker.execute();
} catch (Exception e) {
JOptionPane.showMessageDialog(null, e, "Error", JOptionPane.ERROR_MESSAGE);
}
}
};
timer.scheduleAtFixedRate(task, initialDelay, period);
}
Which is the better method ? Further , the jList blinks once 5 every 5 seconds , how can I avoid that ?
Upvotes: 1
Views: 2067
Reputation: 347194
Swing Timer
executes it's notifications within the context of the Event Dispatching Thread, making it suitable for updating the UI, but not suitable for executing long running or block tasks (like querying a database)
Thread
s are suitable for executing long running or block tasks but should never be used for updating the UI, as Swing is not thread safe and the UI should only be modified from within the context of the EDT. Sure you can use EventQueue.invokeLater
, but it becomes painful if you need to supply parameters to those calls...
You could use a SwingWorker
instead, which has the capacity to execute long running or blocking tasks in the background (off the EDT), but provides means by which you can update the UI, via it's publish
/process
, done
and/or PropertyChange
support
Take a look at:
for more details
Which is the better method
Your first example violates the single thread rules of Swing, so it ain't much use...
Your second example is over kill, the TimerTask
is already running the background, you'd actually be better of using a ScheduledExecutorService
and passing it an instance of the SwingWorker
...
Updated
Not the way I would have liked it to work, but...
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
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;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JLabel label;
private int count;
private ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
public TestPane() {
setLayout(new GridBagLayout());
label = new JLabel("...");
add(label);
service.schedule(new CounterWorker(), 1, TimeUnit.SECONDS);
}
public class CounterWorker extends SwingWorker<Integer, Integer> {
@Override
protected Integer doInBackground() throws Exception {
System.out.println("Tick");
for (int index = 0; index < 100; index++) {
count++;
publish(count);
Thread.yield();
}
return count;
}
@Override
protected void process(List<Integer> chunks) {
label.setText(Integer.toString(chunks.get(chunks.size() - 1)));
}
@Override
protected void done() {
service.schedule(new CounterWorker(), 1, TimeUnit.SECONDS);
}
}
}
}
Upvotes: 6