Timers

Timers are used to perform actions periodically, after an initial delay, or both. Also, actions can be performed only once, if desired.

Here are some reminders regarding the two flavors of timers, as used in Swing applications:

javax.swing.Timer

java.util.Timer

Example 1

The AboutAction class uses a javax.swing.Timer. It's an About dialog which includes a JLabel for displaying the current object heap size. This JLabel is updated every few seconds, to reflect any activity of the garbage collector. (Note that the timer itself does not itself request garbage collection.)

Since this task is not an intensive one (see getHeapSize below), it can be executed directly on the event dispatch thread without causing the GUI to become unresponsive. That is, a Swing timer may be used directly, without a worker thread.

Here are the pertinent parts of the code:


/**
* Display a modal dialog, centered on the main window, which
* contains general information about both this application and 
* the system on which it's running.
*/
public final class AboutAction extends AbstractAction {

  //elided...

  @Override public void actionPerformed(ActionEvent e) {
    fLogger.info("Showing the about box.");    
    showAboutBox();
  }
  
  // PRIVATE

  //elided...
  
  /** Displays the size of the object heap. */
  private JLabel fObjectHeapSize;
  
  /** Periodically updates the display of <tt>fObjectHeapSize</tt>.  */
  private javax.swing.Timer fTimer;
  private ActionListener fHeapSizeUpdater;
  private static final int UPDATE_FREQ = 2 * Consts.MILLISECONDS_PER_SECOND;
  private static final long SLEEP_INTERVAL = 100;
  
  private void showAboutBox(){
    JTabbedPane aboutPane = new JTabbedPane();
    aboutPane.addTab( "About" , getAboutPanel() );
    aboutPane.setMnemonicAt(0, KeyEvent.VK_A);
    aboutPane.addTab( "System Info" , getSystemInfoPanel() );
    aboutPane.setMnemonicAt(1, KeyEvent.VK_S);
    
    startHeapSizeTimer();
    Icon image =  UiUtil.getImageIcon("xray-small.jpg", this.getClass()) ;
    String title = UiUtil.getDialogTitle("About");
    JOptionPane.showMessageDialog(
      fFrame, aboutPane, title, JOptionPane.OK_OPTION, image
    );
    stopHeapSizeTimer();
  }

  /** Periodically update the display of object heap size. */
  private void startHeapSizeTimer(){
    //SwingWorker isn't used here, since the action happens more than once,
    //and the task doesn't take very long
    fHeapSizeUpdater = new ActionListener() {
      public void actionPerformed(ActionEvent evt) {
        //this returns quickly; it won't lock up the GUI
        updateHeapSizeDisplay();
      }
    };    
    fTimer = new javax.swing.Timer(UPDATE_FREQ, fHeapSizeUpdater);
    fTimer.start();    
    fLogger.fine("Starting timer...");
  }
  
  /**
  * Must be called when the About Box is closed - otherwise the timer will continue 
  * to operate.
  */
  private void stopHeapSizeTimer(){
    fLogger.fine("Stopping timer...");
    fTimer.stop(); //stops notifying registered listeners
    fTimer.removeActionListener(fHeapSizeUpdater); //removes the one registered listener
    fHeapSizeUpdater = null;
    fTimer = null;
  }

  private void updateHeapSizeDisplay(){
    fLogger.fine("Updating heap size...");
    fObjectHeapSize.setText(getHeapSize());
  }

  /** Return a measure of the current heap size in kilobytes.*/
  private String getHeapSize(){
    long totalMemory = Runtime.getRuntime().totalMemory();
    long freeMemory = Runtime.getRuntime().freeMemory();
    Long memoryUseKB = new Long( (totalMemory - freeMemory)/Consts.ONE_KILOBYTE );
    StringBuilder result = new StringBuilder();
    result.append(UiUtil.getLocalizedInteger(memoryUseKB));
    result.append(" KB");
    return result.toString();
  }
} 

Example 2

The FetchQuotesAction class uses a javax.swing.Timer to perform a task which is not short lived. This is acceptable, however, since in this case the Timer is combined with a worker thread, and most of the task is not performed on the event dispatching thread.

This is an example of a common requirement - that of performing an intensive, periodic task, using both a timer and worker thread. Another example would be a graphical network management application, which polls the state of many devices every few minutes, and then updates the user interface with the results.

Here are the pertinent parts of the code:


/**
* Fetch current quote data for the {@link CurrentPortfolio} from a data 
* source on the web.
*
* <P>This class performs most of its work in a background thread, 
* using a javax.swing.Timer. The user interface remains responsive, 
* regardless of the time taken for its work to complete.
*/
public final class FetchQuotesAction extends AbstractAction implements Observer {

  //elided...

  /**
  * Start an internal Timer, which in turn calls {@link #actionPerformed(ActionEvent)}.
  * 
  * <P>This method must be called immediately after calling the constructor. 
  * (Since this operation uses a 'this' reference, it shouldn't be included in the 
  * constructor itself.)  
  */
  public void startTimer(){
    fQuoteTablePrefEditor.addObserver(this);
    fCurrentPortfolio.addObserver(this);
    fTimer = new javax.swing.Timer(fUpdateFreq * CONVERSION_FACTOR, this);
    fTimer.start();
  }

  /** 
   Fetch quotes from the web for the <tt>CurrentPortfolio</tt>.
   This is called either explicitly, or periodically, by a Timer.
  */
  @Override public void actionPerformed(ActionEvent e) {
    fLogger.info("Fetching quotes from web.");
    fSummaryView.showStatusMessage("Fetching quotes...");
    SwingWorker<List<Quote>, Void> hardWorker = new HardWorker();
    hardWorker.execute();
  }
  
  // PRIVATE 

  /**
  * The set of {@link Stock} objects in which the user 
  * is currently interested.
  */
  private CurrentPortfolio fCurrentPortfolio;
  
  /**
  * Periodically fetches quote data.
  *
  * <P>Use of a Swing Timer is acceptable here, in spite of the fact that the task
  * takes a long time to complete, since the task does <em>not</em> in fact get 
  * executed on the event-dispatch thread, but on a separate worker thread.
  */
  private javax.swing.Timer fTimer;
  
  /** The number of minutes to wait between fetches of quote information. */
  private int fUpdateFreq;
  
  private static final int CONVERSION_FACTOR = 
    Consts.MILLISECONDS_PER_SECOND * Consts.SECONDS_PER_MINUTE
  ;

  //elided...

  private final class HardWorker extends SwingWorker<java.util.List<Quote>, Void> {
    @Override  protected List<Quote> doInBackground() throws Exception {
      List<Quote> result = null;
      try {
        result = fCurrentPortfolio.getPortfolio().getQuotes();
      }
      catch(DataAccessException ex){
        ex.printStackTrace();
      }
      return result;
    }
    @Override protected void done() {
      try {
        //access the data returned by doInBackground():
        List<Quote> quotes = get();
        if (quotes != null){
          showUpdated(quotes);
        }
        else {
          fSummaryView.showStatusMessage("Failed - Please connect to the web.");
        }
      }
      catch (Exception ex) {
        ex.printStackTrace();
      }
    }
  }

  private void showUpdated(List<Quote> aQuotes) {
    fQuoteTable.setQuoteTable(aQuotes);
    fSummaryView.setQuotes(aQuotes);
    StringBuilder warning = new StringBuilder();
    if (hasNoZeroPrices(aQuotes, warning)){
      fSummaryView.showStatusMessage("Done.");
    }
    else {
      fSummaryView.showStatusMessage(warning.toString());
    }
  }
}
 

Example 3

For a simple example using java.util.Timer and java.util.TimerTask in a non-Swing context, please see this related topic.

See Also :
Schedule periodic tasks
Swing threads
Would you use this technique?
Yes   No   Undecided   
© 2014 Hirondelle Systems | Source Code | Contact | License | RSS
Individual code snippets can be used under this BSD license - Last updated on September 21, 2013.
Over 2,000,000 unique IPs last year - Built with WEB4J.
- In Memoriam : Bill Dirani -