Swing programmers are faced with the same problems no matter the difference in the domain applications for their software. Thankfully these problems are so common that the giants on whose shoulders we stand have already thought out some generic ideas and solutions to them.
Do not disrespect the EDT
There are generally two types of toolkits, namely multi threaded toolkits and single threaded toolkits. It is currently believed that multi threaded graphical toolkits are either too difficult to implement or if implemented, less natural for programmers to use correctly without creating threading related bugs.
In single threaded GUI toolkits, all GUI activity is run on one thread, the Event Dispatch Thread. Any updates to the interface (including repaints) and reactions to those updates (event-handling) are run as discrete events queued on this E.D.T. This model is both simple to implement and easier for programmers to use correctly than the multi threaded model.
Swing uses a single threaded model. This is the first important thing to know about swing. There are many bugs in swing programs that are attributable to forgetting (or not knowing) this. There are generally two ways of disrespecting the E.D.T:
a) Running long tasks on the E.D.T.
As I mentioned above event-handling code is run on the EDT. Since the EDT runs its tasks one after the other (single threaded), any task running on it will block any other tasks (which run on the EDT) from doing anything. This means that when the event-handling code is running, no input can be accepted from the user and the interface freezes. Event-handling code thus needs to be very short, perhaps not longer than half a second. If the listener code needs to be longer then it needs to be kicked off in its own thread. Such threads are usually known as worker threads or background thread.
Considerations
SwingWorker
• Sometimes you may actually want to stop users from doing anything until a certain task is completed. The correct way of doing this is to call a disableInput method somewhere, kick off the task in its own thread and display a progress bar.
• Some insight is required in deciding whether tasks are long or not because some tasks may appear short during testing but may become very long in production when real data is factored in. e.g. some database operations’ time is dependent on the size of the database.
• If the listener code is running in its own thread but needs to update the interface then we have a small problem because all interface updates need to be done on the EDT. A solution is simply to queue such updates on the EDT using either SwingUtilities.invokeLater or SwingUtilities.invokeAndWait as required which simply queues tasks in a Runnable’s run method onto the EDT.
• Another consideration with worker threads is allowing users to have control on how the background tasks are run. Typically it may be required that users be able to pause, cancel, restart or stop the background process. This requires special care because the task control should be made available on the EDT while the task being controlled is being run in a separate (worker) thread.
It turns out though that it is possible to have a generic solution for the problems of updating the GUI from worker threads and giving users the ability to control the background tasks. The solution is the javax.swing.SwingWorker class. As of Java SE 6, this class is available as part of the JDK under the javax.swing package. Prior to that, programmers had to write their own SwingWorker class. Documentation for both these surprisingly quite different classes is readily available.
The SwingWorker class provides a means of returning intermediate results from the background task and also provides a method to use for updating the interface with those intermediate results. It also solves some possible memory inconsistency errors that may occur caused by the EDT thread and the worker thread accessing the same object.
b) Updating the GUI from other threads besides the EDT.
This has already been mentioned above. There is need for care when writing any code that updates the GUI to make sure that it is always run from the EDT because most of the swing methods are not thread safe. If you are not sure if certain code will run on the EDT, then you can use the SwingUtilities.isEventDispatchThread method before performing the GUI related tasks. If the call returns false then you need to use SwingUtilities.invokeLater to queue the GUI tasks on the EDT.
A small but useful exception to this rule is the setText method defined in javax.swing.text.JComponet which is specified as thread safe. A common error is to create the GUI elements from the initial thread i.e. from the main method. The code that runs in the main method is run by a special thread whose only special property for the purposes of this discussion is that it is not the same as the EDT. All the GUI initializing code should instead be bundled in a method which is called from the run method of a thread queued on the EDT. SwingUtilities’ invokeAndWait is the common method of doing this as explained in Sun’s Java tutorial.
Expand|Select|Wrap|Line Numbers
- SwingUtilities.invokeLater(new Runnable()) {
- public void run() {
- createAndShowGUI();
- }
- }
Never mix AWT and Swing.
Do not mix those two the same way you never put lemon in coffee with milk. AWT components are not implemented in Java but use native peer components. This means that the AWT components will look different on different platforms. Their reliance on the platform however, also means that they are faster than Swing albeit only fractionally and increasingly becoming less noticeable. By contrast, Swing components are written in Java (hence the J before the component names) and can be made to look the same on all platforms.
The small speed difference and different looks are not the main reasons you should never mix them. The z-ordering schemes of the two are different and so there are many painting problems that occur when their components are mixed. Basically things will not display where you expected them to display and some components will hide behind others for no immediately apparent reason. There is no need to use AWT components when using Swing anyway because Swing is a component wise superset of AWT.
Design with i18n and L10N in mind
Swing programs contain a lot of locale sensitive objects. Those objects need to be made localizable by separating them from the source code. Things like new JButton(“OK”) should not be used because the label “OK” can be required to be changed into another language (or another English word). Icons too may mean different things in different environments. Basically any text that is visible to the user must not be hard coded in code so as to be made changeable, perhaps even by the users themselves, without the need for the programmer or for a redeploy of the application. There are several classes in the Java SE which assist in internationalization and localization of programs.
Design with Accessibility in mind
• Messages must always come from ResourceBundles. The ResourceBundles themselves need to be carefully designed so as not to duplicate entries unnecessarily.
• Avoid concatenating Strings intended for user display. Some languages are read from right to left while others are read left to right. You can easily see why concatenation presents a problem.
• Message processing operations must always be done using Collaters. If there are lots of comparisons to be made then CollationKeys should be used to improve performance.
• Formatting of displayable objects must always be done in a locale sensitive manner.
Accessibility should not be considered only with reference to people with disabilities. Physically disabled or not, people are just different. They have different color and font preferences and some prefer different input devices to others. To make matters worse, input interfaces to computers are increasingly becoming more and more exotic as computers are now being integrated into all sorts of gadgets. Writing a software program that satisfies all these different people and environments might sound like a daunting task. Two general principles will make your software very accessible.
Standard Java ships with a small accessibility API that you can use to make your software more accessible. The API generally allows you to program to some unknown input and output devices without assuming a particular implementation of those interfaces. That means your code will work on any exotic input devices that implement those interfaces.
• When writing support for a particular input device, try to make the support as complete as possible. E.g. Keyboard support should be so comprehensive that all the system functionality can be realized by using the keyboard only.
• Spend more time writing code for user configurable components rather than writing code to configure components.
Sun’s tutorial lists some rules for supporting accessibility. .
Data binding
You will typically need to make use of persistent data in your swing application. You may need to use data from files or (more commonly) databases. Database access can be done easily using the JDBC API. There is need for a clear separation between data access code and interface construction code. In particular, SQL statements should never be found in any classes that use swing components.
This separation of concerns is usually best achieved through implementing some pattern, or more realistically, combination of patterns. Some patterns make it possible to switch between different data source types e.g. different RDBMs like the DAO . Your DAO will typically be observable by your Swing components (observers) so that they can respond appropriately when the model changes. This scenario lends itself naturally to the popular Model View Controller architecture. Notice that Swing does not use pure MVC though it is strongly aligned with it and can be considered MVC for most practical purposes. Most Swing components have models where you can enforce pure MVC easily by setting data to those component models rather than to the components themselves, or better yet, implementing (or more commonly extending) those models yourself. All that is required is that you satisfy the contracts of those models' interfaces. This has the advantage of allowing different representations for the same model to be correctly updated when their (common) underlying model is changed.
Event handling
Favor the use of Actions and the AbstractAction class over having actionListeners all over the place. The advantage of using actions is that they allow a neat separation of functionality and state. Using actions also make it easy to enable/disable functionality without duplicating code for components which use that same action. Action classes are also visible to long term persistence mechanisms and so allow easier integration of persistence into your program. The slight drawback is that since the actions use the command pattern, you need a class for each action.
Construct the interface components in an intelligent way
Some things just require a bit of the thought process. Have a set of utility classes with methods that do common tasks for you. Sun used the same trick with the SwingUtilities class. Make good acquaintance with that class so that you don't have to keep reinventing the wheel. Avoid writing code to decorate each component, rather have decorators that decorate certain types of components.
Construct components only when absolutely necessary and be reluctant to completely destroy large components when a similar component may need to be displayed.
Nielsen’s heuristics
GUI development still has an artistic side that should not be overlooked. Regrettably, most programmers are not well gifted in artistic qualities. We are mostly fact driven geeks who don’t appreciate the difference between the Mona Lisa and our grandmother’s portrait hanging on the wall (no disrespect meant to our grandmothers of course, God bless their souls). If you were like me and spent most of your high school time fiddling around with bits when others were visiting art galleries, then you may not have that extra artistic edge required in GUI designing. I find Nielsen's heuristics particularly helpful as design guidelines. They are not hard and fast rules and indeed can be considered to be contradictory in some respects but they help avoid some elementary design blunders.
Conclusion
Some of the advice in this article may not be viable due to the nature of the application being created and you may wish to use an approach contrary to what has been suggested. The important thing is that at least you understand those suggestions and justify the need for an alternative approach. There are so many options available to Swing programmers, too many in my opinion because some of them just promote dirty solutions. I hope the article makes sense and is in some way useful to you.