Here is a simple SwingWorker example to save text in JTextArea to a file. Before we go into the program, let me introduce the SwingWorker. The use of the swing worker is to improve the performance of the GUI application by leaving the non GUI part (or the part that doesn't need to run in the EDT) to a thread called the worker thread. If the non GUI part (usually the lengthy back end stuff) runs in the EDT, then the GUI might go unresponsive. The kegiatan discussed below, however it isn't a perfect example because saving a simple text file isn't a big task for most computers today. However, this is just an attempt to show what is SwingWorker and how it smooths our life.
The
The next parameter is the type of the intermediate (aka interim) result produced. An interim result is an intermediate result which might be useful in the future. As there is no intermediate results in this example, the second type is left to Void.
The
After the
The first version of the
The second version throws the above two exceptions along with the TimeoutException. It is wise to understand the difference between the
The first version of the get() method waits until you get the result from the
To overcome such situations, the second version of the
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.concurrent.*;
import javax.swing.event.*;
class SwingWorkerExample extends JFrame
{
JPanel jp;
JButton save;
JTextArea jt;
JScrollPane js;
public SwingWorkerExample()
{
createAndShowGUI();
}
private void createAndShowGUI()
{
// Set frame properties
setTitle("Save a file");
setDefaultCloseOperation(EXIT_ON_CLOSE);
// Create a JPanel
jp=new JPanel();
// Set FlowLayout for jp
jp.setLayout(new FlowLayout());
// Create save button
save=new JButton("Save");
// Create JTextArea
jt=new JTextArea();
// Create scroll pane for jt
js=new JScrollPane(jt);
// Add the JScrollPane to center
add(js);
// Add the button to jp
jp.add(save);
// Add the JPanel to NORTH
add(jp,BorderLayout.NORTH);
// Set an action for the save button
save.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent ae)
{
// Call the save() method to start saving
save();
}
});
// To make the save button enabled every time there is
// a modification of text in the JTextArea's current document
jt.getDocument().addDocumentListener(new DocumentListener(){
public void insertUpdate(DocumentEvent de)
{
enableButton();
}
public void changedUpdate(DocumentEvent de)
{
enableButton();
}
public void removeUpdate(DocumentEvent de)
{
enableButton();
}
});
setSize(400,400);
setLocationRelativeTo(null);
setVisible(true);
}
public void enableButton()
{
// Set the text and enable
// the button
save.setText("Save");
save.setEnabled(true);
}
private void save()
{
// The worker thread that takes Integer,Void
// Parameter desc
// ---------------
// The first type (here Integer) is the return type of
// the doInBackground() method.
// The next type (here Void) is the intermediate result
// type, if there is no intermediate result, then it is set
// to Void type.
// The return type of the get() method is the return type
// of the doInBackground() method
SwingWorker<Integer,Void> worker=new SwingWorker<Integer,Void>(){
// The Integer returned here is either -1 or 0
// representing unsuccessful and successful save
// respectively.
protected Integer doInBackground()
{
// Disable the button
save.setEnabled(false);
System.out.println("doInBackground()");
// Save the text as saving
// This text is not mostly seen, because
// the process is done so faster, that the
// with in less time, done() method is reached
// For a larger file, you can see the text
// Remember, save.setText() isn't thread safe
// It is a GUI operation that repaints the button
// So, execute it in the EDT
SwingUtilities.invokeLater(new Runnable(){
public void run(){
save.setText("Saving..");
}
});
// Create a File object
File f=new File("saved.txt");
try
{
// Now do the operation
jt.write(new FileWriter(f));
}catch(Exception e){
// When there occurs an exception, the file
// isn't saved, so return -1
return -1;
}
// If no exception occurs, the file is saved
// successfully, meaning 0 should be returned
return 0;
}
// This method is executed once after, the
// doInBackground() method is executed
// done() runs in the EDT
protected void done()
{
System.out.println("done()");
// The get() method throws various exceptions
// More on it in the post page
try
{
// If savigs is not done within 2 seconds
int i=get(2L,TimeUnit.SECONDS);
// If 0 is returned, file is saved
if(i==0) save.setText("Saved.");
// Else, file isn't saved
// One way to see this text is to set the
// saved.txt to Read only
else save.setText("Unable to save.");
// Using multi catch specification
}catch(InterruptedException|ExecutionException|TimeoutException e){
// If saving doesn't happen within the time, time is out.
// i.e. a TimeoutException is thrown
if(e instanceof TimeoutException) {
save.setEnabled(true);
save.setText("Save again.");
}
}
}
};
// Start the worker thread
worker.execute();
}
public static void main(String args[])
{
SwingUtilities.invokeLater(new Runnable(){
public void run()
{
new SwingWorkerExample();
}
});
}
}
The
SwingWorker
class is a generic class taking two types generic parameters. The first parameter is the return type of the doInBackground()
method and also the get()
method. This return type is the type of the simpulan result produced by the doInBackground()
method. The next parameter is the type of the intermediate (aka interim) result produced. An interim result is an intermediate result which might be useful in the future. As there is no intermediate results in this example, the second type is left to Void.
The
doInBackground()
method contains the code that needs to run in the background (the code that doesn't interact with the GUI or the non-thread-safe part of the swing) is written here. This method is not executed in the EDT, instead a separate thread does the job.After the
doInBackground()
method is complete, the done() is called. This done() method is executed in the EDT because so that the programmer can show the updated status on the GUI components. The method get()
which returns the value returned by the doInBackground()
method throws several exceptions. The exceptions and their causes are given below. But before that, you need to learn that there are two overloaded versions of the get()
method, get()
and get(long l,TimeUnit)
out of which second one is the most preferred to ensure smoother GUI performance.The first version of the
get()
method throws two exceptions which are, InterruptedException
- if the thread is interrupted before there is a result, ExecutionException
- exception in computation.The second version throws the above two exceptions along with the TimeoutException. It is wise to understand the difference between the
get()
and the get(long l,TimeUnit units)
method.The first version of the get() method waits until you get the result from the
doInBackground()
method. If you didn't get a result from the doInBackground() method due to thread interruption, then the get()
method waits, the kegiatan is blocked until doInBackground()
returns a value. As this method is called in the EDT, the GUI of the kegiatan goes unresponsive.To overcome such situations, the second version of the
get()
method is used in which we can specify the time (either in seconds, minutes, milliseconds or whatever) before which the we need to get the computation result. If we don't get the result within the specified time, the control goes on, thereby leaving the GUI responsive. Here, the time is set to 2 seconds. Which means that, here if i don't get a value (either 0 or -1) within 2 seconds, then a TimeoutException
is thrown.