Subscribe to JVM Technologies Blog        RSS Feed
***** 1 Votes

The Evolution of the Java GUI

Icon 6 Comments
The Java GUI is one element of the standard library that has greatly evolved with the language. The purpose of Java is to allow programs to run, regardless of the operating system, and the Java GUI was built around this principal. The first GUI framework Sun developed was called AWT, or Abstract Windows Toolkit. In order to remain platform independent, AWT was designed with a least-common denominator approach. That is, only Components and features that worked on all supported systems were included. This made for a very sparse framework, where developers had to create many of their own custom components. AWT Components also rely on the native system for painting, which created platform-specific behaviors and bugs. In fact, with the exception of Applets, most AWT Components with corresponding JComponents have been antiquated by the Swing updates. The reason Applets are arguably favored over JApplets is based on limited support by browser plugins for Swing, a problem which I personally have yet to encounter on any modern browser.

Swing was released with Java 1.2 as an upgrade from AWT. Swing is inherantly built upon the AWT framework, but handles its painting internally rather than natively, eliminating many of the platform specific glitches of AWT, as well as allowing for a much more powerful GUI framework. Some of the major components made possible by Swing include JTable, JTree, and JProgressBar. The one drawback to Swing is that because the painting is handled by Java, Swing JComponents are non-Thread safe. Accessing JComponents from multiple Threads can cause ConcurrentModificationExceptions, which often times freezes up the GUI. However, there are certain methods, like the JTextArea append() method, that are Thread-safe.

Before we discuss more on the Java GUI and Concurrency, it is important to understand some of the architecture behind Swing and AWT. Backing the Java GUI is a main Thread called the Event Dispatch Thread. The Event Dispatch Thread is responsible for running code associated with the GUI. This means GUI elements, including Listeners. It is also the Thread that accesses the GUI elements, so there is no violation of the one-thread rule. In order for the Event Dispatch Thread to interact with the Listeners, they need to be hooked properly into the Thread. This is accomplished by adding the Listener to the appropriate GUI Component. So for example, if we use the JButton addActionListener() methtod for a specific JButton, we are hooking the ActionListener into the Event Dispatch Thread, as well as associating it with the specific Component.
JButton b = new JButton("Click Me");

//this section of code performs multiple tasks
//including assigning an ActionListener to respond
//to the JButton's clicks, as well as tying it into the
//Event Dispatch Thread
b.addActionListener(new ActionListener(){
     public void actionPerformed(ActionEvent e){
          JOptionPane.showMessageDialog(null, "You clicked the button!");
     }    
});




Even though Swing is not Thread-safe, there are options for concurrency in Swing. Perhaps one of the most common reasons developers want to use Threads with Swing is for Game Programming, as that is a common practice in other languages like C++. In Java, better practice is to use a Swing Timer instead. Unlike Threads which run indefinitely, Swing Timer is event-driven, meaning it fires an event on an interval, as opposed to sleeping for a period of time. This event is then processed by an ActionListener. So the general logic is to update the necessary data, then update the GUI. Below is a demo program to illustrate this concept. Basically, it moves a ball across the screen. The Swing Timer updates the position every 30 milliseconds, then invokes repaint(), which in turn calls paint(). The paint() method redraws the ball at the new coordinates.
//extend JPanel to handle the painting
public class MyPanel extends JPanel{
 
       //the x and y coordinates of the ball
     private int x, y; 


     private Timer t;

     public MyPanel(){
         x = 25;
         y = 50;

         //create a Swing Timer to modify the coordinates
         //of the sphere and invoke repaint() once every 30 ms
         //also note how I add an ActionListener to the Timer
         //this works the same way as when I added the ActionListener
         //to the JButton
         Timer t = new Timer(30, new ActionListener(){
             public void actionPerformed(ActionEvent e){
                x += 3;
                y += 3;
                repaint();
             }
         });

         t.start();
     }

     public void paint(Graphics g){
          super.paint(g);
     }
}



While Swing Timer is good for basic games and GUIs, it only allows for pseudo-concurrency, meaning that even though the results are often times the same as if Threads were used, the means are different, as Swing Timer runs on the Event Dispatch Thread. The results of this become especially noticeable when there are multiple Swing Timers. When they overlap, their calls are all queued rather than executed simultaneously.

There is a solution, though, that allows for safe concurrency with Swing. This tool is called SwingWorker. The SwingWorker class is what's called a Background Thread, and as such, runs in the background. It was designed to allow developers to delegate heavy-duty computation so as not to slow the GUI down substantially. So for example, if a program needs to import multiple Files at the same time, delegating each File to a SwingWorker can help speed up the import process and reduce wait time for the user. Basically, to work with SwingWorker, you need to extend it and implement the doInBackground() method, as SwingWorker is abstract. In comparing this to Threads, the doInBackground() method in SwingWorker is comparable to the run() method in Thread. It is also good practice to override the done() method to handle cleanup work in the SwingWorker.
class MyWorker extends SwingWorker<K, V>{
 
     public K doInBackground(){
        //code to be run in background
     }

     public void done(){
        //code to handle cleaning up
     }
}

6 Comments On This Entry

Page 1 of 1

m-e-g-a-z Icon

09 December 2010 - 02:09 PM
Nice blog post macosxnerd101, a good read. :)
0

CoderOfWorlds Icon

09 December 2010 - 04:01 PM
Great read very informative
0

dorknexus Icon

09 December 2010 - 07:35 PM
i don't believe in evolution so i am going to have to reject this narrative.
1

dawmail333 Icon

10 December 2010 - 09:34 PM

Dark_Nexus, on 10 December 2010 - 11:35 AM, said:

i don't believe in evolution so i am going to have to reject this narrative.

:P
0

bcranger Icon

10 December 2010 - 10:15 PM
:^:
0

blackcompe Icon

01 September 2011 - 03:33 PM
This is great. I learned something new.
0
Page 1 of 1

Trackbacks for this entry [ Trackback URL ]

There are no Trackbacks for this entry

April 2014

S M T W T F S
  12345
6789101112
13141516171819
20212223 24 2526
27282930   

Recent Entries

Recent Comments

Search My Blog

0 user(s) viewing

0 Guests
0 member(s)
0 anonymous member(s)