Swing is not thread safe as discussed with previous article. That means that any task which can take long time in execution should not be performed in Event Dispatcher Thread (EDT). Otherwise the EDT will be blocked and so the whole UI. So the solution, to perform the time intensive operations, is:
- Start a new thread from EDT
- Perform the heavy, time consuming task in this new thread
- When task is done, update the Swing UI state or call any operation in EDT
- EDT will invoke the operation
To perform above steps, we need to create a thread for time consuming task and also need to devise a mechanism to invoke the Swing Operation in EDT once the task is done (inter-thread communication). This involves some of the multi-threading code and may need to repeat it again and again with every such requirement. To abstract this code, and simplify this task; Java provides a SwingWorker class to hide all the complexities and hence the repeated work to perform these operations. It is an abstract class, which should be extended and some of its methods should be overridden to get the work done.
SwingWorker class has following important methods.
execute()
This method is the main method which should be called to start the work of SwingWorker class. As soon as you call this method, SwingWorker will give call to do the time consuming task in a new thread (described below).
doInBackground()
This method should be overridden by developer to do the time consuming task. This method will be called in a new thread, other than EDT, by SwingWorker. So any task performed on this thread won't affect the Swing UI operations. Once this method is done, result will be retained by SwingWorker which can be asked by using get() method. publish() method can also be called from this method to make the intermediate results available for GUI operations.
process()
It should be overridden if we want to process any data in EDT, as and when this data becomes available during doInBackground() operation. Suppose, one program is to load the heavy file from network which is being done in doInBackground method. Depending upon the data downloaded, you want to show the progress bar or some %age downloaded in a label on UI. This can be done by putting the work to update the label or progress bar in 'process' method. From 'doInBackground' method, we need to call the 'publish' method with the values which we want to publish; like %age data downloaded in this case. SwingWorker will call process method with this data in EDT. process method then can update the UI label. One more important point, SwingWorker may coalesced the multiple calls of publish in one call to process method, if previous calls were not executed till now. So if we are calling publish with suppose '%10', '%30', and then with '%50'; it might be possible that process method is called first with '10%' and then with '30%, 50%'. Process method should handle this case.
done()
This method is called by SwingWorker when task defined in 'doInBackground' method is finished. This is a place where final Swing UI updates should be done based on outcome from 'doInBackground' method. This method is called in EDT.
Object get()
This method is used to retrieve the result of task performed by 'doInBackground' method. Point to consider, that this method is a blocking method. So calling thread will be blocked till result is not available. So it must be used carefully, as if it is called from EDT, it will block the UI operations. One of the approach to use it could be to show model dialog box till result is received.
This way, SwingWorker class save us from defining the thread safe, and multi-threaded code again and again and work as a great utility class.