问题
I have a GUI app that is getting to be quite slow. I want to start introducing timing for various GUI tasks - however, many of our GUI actions trigger other actions which then "invoke later" to trigger other actions.
Eventually, it all settles down and there's nothing more to do. At this time, I want to stop a timer and report how long that GUI "action" took.
I figured the way to do this is to implement a method called invokeOnceIdle(Runnable task)
. The method will execute the supplied task only once the AWTEventQueue is "empty". i.e. the supplied "task" should be the last thing in the queue.
One way to do this would be is if there's a way to specify a "lowest" priority to SwingUtilities.invokeLater
- but this isn't possible.
I next looked to see if I could "invokeLater" a Runnable which checks to see if the event queue is "empty" - but there's no public way to see if the event queue is actually empty.
What's the best way to do this?
回答1:
Using your own event queue, you can easily reach that goal. Here something that I cooked up and should get you going:
private static class MyEventQueue extends EventQueue {
private Deque<Runnable> onceIdle = new LinkedList<Runnable>();
public MyEventQueue() {
Toolkit.getDefaultToolkit().getSystemEventQueue().push(this);
}
public void runOnceIdle(Runnable toRun) {
onceIdle.addLast(toRun);
}
@Override
protected void dispatchEvent(AWTEvent event) {
super.dispatchEvent(event);
if (peekEvent() == null) {
for (Runnable toRun : onceIdle) {
toRun.run();
}
onceIdle.clear();
}
}
}
All you have to do is push your "Once idle" runnables to the instance of the EventQueue using runOnceIdle()
回答2:
isEventDispatchThread
returns if is current AWTEventQueue
is empty or not,
- if is current
AWTEventQueue
is empty you can to post a new event(s) to EventQueue from
invokeAndWait()
and invokeLater
too
- if is current
AWTEventQueue
is not empty then you can not to useinvokeAndWait()
onlyinvokeLater()
for example
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
public class IsThereEDT {
private ScheduledExecutorService scheduler;
private AccurateScheduledRunnable periodic;
private ScheduledFuture<?> periodicMonitor;
private int taskPeriod = 30;
private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
private Date dateRun;
private JFrame frame1 = new JFrame("Frame 1");
public IsThereEDT() {
scheduler = Executors.newSingleThreadScheduledExecutor();
periodic = new AccurateScheduledRunnable() {
private final int ALLOWED_TARDINESS = 200;
private int countRun = 0;
private int countCalled = 0;
private int maxCalled = 10;
@Override
public void run() {
countCalled++;
if (countCalled < maxCalled) {
if (countCalled % 3 == 0) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
System.out.println("Push a new event to EDT");
frame1.repaint();
isThereReallyEDT();
}
});
} else {
if (this.getExecutionTime() < ALLOWED_TARDINESS) {
countRun++;
isThereReallyEDT(); // non on EDT
}
}
} else {
System.out.println("Terminating this madness");
System.exit(0);
}
}
};
periodicMonitor = scheduler.scheduleAtFixedRate(periodic, 0, taskPeriod, TimeUnit.SECONDS);
periodic.setThreadMonitor(periodicMonitor);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
isThereReallyEDT();
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.getContentPane().add(new JLabel("Hello in frame 1"));
frame1.pack();
frame1.setLocation(100, 100);
frame1.setVisible(true);
}
});
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
Logger.getLogger(IsThereEDT.class.getName()).log(Level.SEVERE, null, ex);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame2 = new JFrame("Frame 2");
frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame2.getContentPane().add(new JLabel("Hello in frame 2"));
frame2.pack();
frame2.setLocation(200, 200);
frame2.setVisible(true);
isThereReallyEDT();
}
});
}
private void isThereReallyEDT() {
dateRun = new java.util.Date();
System.out.println(" Time at : " + sdf.format(dateRun));
if (EventQueue.isDispatchThread()) {
System.out.println("EventQueue.isDispatchThread");
} else {
System.out.println("There isn't Live EventQueue.isDispatchThread, why any reason for that ");
}
if (SwingUtilities.isEventDispatchThread()) {
System.out.println("SwingUtilities.isEventDispatchThread");
} else {
System.out.println("There isn't Live SwingUtilities.isEventDispatchThread, why any reason for that ");
}
System.out.println();
}
public static void main(String[] args) {
IsThereEDT isdt = new IsThereEDT();
}
}
abstract class AccurateScheduledRunnable implements Runnable {
private ScheduledFuture<?> thisThreadsMonitor;
public void setThreadMonitor(ScheduledFuture<?> monitor) {
this.thisThreadsMonitor = monitor;
}
protected long getExecutionTime() {
long delay = -1 * thisThreadsMonitor.getDelay(TimeUnit.MILLISECONDS);
return delay;
}
}
来源:https://stackoverflow.com/questions/11204577/how-to-implement-idle-task-in-java-swing