Swing programming in Java USES the SwingWorker thread pattern and the top level container

  • 2020-04-01 04:38:03
  • OfStack

Use SwingWorker thread mode

Careful use of concurrency is important for Swing developers. A good Swing program USES concurrency to create user interfaces that don't lose their responsiveness - no matter what user interaction is, the program can always respond to it. To create a responsive program, developers must learn how to use multithreading in the Swing framework.
A Swing developer will deal with several types of threads:
(1) Initial threads, which will execute the Initial application code.
(2)The event dispatch thread, where all The event handling code is executed. Most code that interacts with the Swing framework must also execute this thread.
(3) Worker threads (Worker threads), also known as background threads (background threads), will execute all the time-consuming tasks.
The developer does not need to explicitly create these threads in the code: they are provided by the runtime or Swing framework. The developer's job is to use these threads to create responsive, durable Swing programs.
Like all other programs running on the Java platform, a Swing program can create additional threads and thread pools, using the methods described in this article. This article covers all three of these threads. The discussion of worker threads will involve using the javax.swing.swingworker class. This class has many useful features, including communication and collaboration between worker thread tasks and other thread tasks.
1. The initial thread
Each program generates a series of threads at the beginning of the application logic. In a standard program, there is only one such thread: this thread will call the main method in the main class of the program. In an applet, the initial thread is the constructor of the applet object, and it calls the init method. These actions may be executed in a single thread, or in two or three different threads, depending on the implementation of the Java platform. In this article, we call these threads initial threads.
In a Swing program, the initial thread doesn't have much to do. Their most basic task is to create a Runnable object that initializes the GUI and orchestrates the order of the objects that are used to execute the events in the event dispatch thread. Once the GUI is created, the program is driven primarily by GUI events, each of which causes an event to be executed in the event dispatch thread. Program code can orchestrate additional tasks to event-driven threads (provided they are executed quickly so that they do not interfere with the processing of the event) or create worker threads (to perform time-consuming tasks).
An initial thread scheduling GUI to create task is by calling the javax.mail. Swing. SwingUtilities. InvokeLater or javax.mail. Swing. SwingUtilities. InvokeAndWait. Both methods take a unique parameter: Runnable is used to define the new task. The only difference is that invokerLater simply orchestrates tasks and returns them; InvokeAndWait waits for the task to complete before it returns.
See the following example:


SwingUtilities.invokeLater(new Runnable()) {
 public void run() {
  createAndShowGUI();
 }
}

  In an applet, the task of creating a GUI must be put into the init method and invokeAndWait is used; Otherwise, the initial process may be completed before the GUI is created, which can lead to problems. In other cases, the choreography GUI creation task is usually the last to be executed in the initial thread, so use either invokeLater or invokeAndWait.
Why doesn't the initial thread create the GUI directly? Because almost all of the code used to create and interact with Swing components must be executed in the event dispatch thread. This constraint will be discussed in the following article.
  2. Event dispatch thread
The Swing event handling code is executed in a special thread called the event dispatch thread. Most of the code that calls Swing methods is executed in this thread. This is necessary because most Swing objects are "non-thread-safe."
You can think of code execution as a series of short tasks in the event dispatch thread. Most of the tasks is called event handling methods, such as ActionListener. ActionPerformed. The remaining tasks are choreographed by the program code, using either invokeLater or invokeAndWait. The task in the event dispatch thread must be able to be executed quickly, otherwise the user interface will become "unresponsive" due to a backlog of unprocessed events.
If you need to determine whether your code is distributed in the event thread of execution, callable javax.mail. Swing. SwingUtilities. IsEventDispatchThread.
  3. Worker thread with SwingWorker
When a Swing program needs to perform a long task, it usually USES a worker thread to complete it. Each task is executed in a worker thread, which is an instance of the javax.swing.swingworker class. The SwingWorker class is an abstract class; You must subclass it to create a SwingWorker object; Anonymous inner classes are often used to do this.
SwingWorker provides some communication and control features:
(1) a subclass of SwingWorker can define a method, done. When the background task completes, it is automatically called by the event dispatch thread.
(2) the SwingWorker class implements the Java. Util. Concurrent. The Future. This interface allows background tasks to provide a return value to other threads. Methods in this interface also provide the ability to undo background tasks and determine whether background tasks have been completed or undone.
(3) the background task can provide intermediate results by calling swingworker.publish, which will be called by the event dispatch thread.
(4) background tasks can define binding properties. Changes to the binding properties trigger events, and the event dispatch thread invokes event handlers to handle these triggered events.
4. Simple background tasks
Here is an example of a very simple task, but one that is potentially time consuming. The TumbleItem applet imports a series of image files. If these image files are imported through the initial thread, there will be a delay before the GUI appears. If these image files are imported in the event dispatch thread, it is possible that the GUI will temporarily fail to respond.
To solve these problems, the TumbleItem class creates and executes an instance of the StringWorker class when it is initialized. The doInBackground method of this object, executed in a worker thread, imports the image into an ImageIcon array and returns a reference to it. Then the done method, executed in the event dispatch thread, gets the reference returned and places it in the member variable imgs of the applet class. This allows the TumbleItem class to create the GUI immediately without waiting for the image import to complete.
The following sample code defines and implements a SwingWorker object.


SwingWorker worker = new SwingWorker<ImageIcon[], Void>() {
 @Override
 public ImageIcon[] doInBackground() {
  final ImageIcon[] innerImgs = new ImageIcon[nimgs];
  for (int i = 0; i < nimgs; i++) {
   innerImgs[i] = loadImage(i+1);
  }
  return innerImgs;
 }
 
 @Override
 public void done() {
  //Remove the "Loading images" label.
  animator.removeAll();
  loopslot = -1;
  try {
   imgs = get();
  } catch (InterruptedException ignore) {}
  catch (java.util.concurrent.ExecutionException e) {
   String why = null;
   Throwable cause = e.getCause();
   if (cause != null) {
    why = cause.getMessage();
   } else {
    why = e.getMessage();
   }
   System.err.println("Error retrieving file: " + why);
  }
 }
};

        All subclasses that inherit from SwingWorker must implement doInBackground; Implementing the done method is optional.
Note that SwingWorker is a generic class with two parameters. The first type parameter specifies the return type of doInBackground. It is also the type of get method that can be called by other threads to get the return value from doInBackground. The second type parameter specifies the type of the intermediate result. This example does not return the intermediate result, so it is set to void.
Using the get method, you can make a reference to the object imgs (created in the worker thread) available in the event dispatch thread. This allows you to share objects between threads.
There are actually two methods to get the object returned by the doInBackground class.
(1) call swingworker.get without parameters. If the background task is not completed, the get method blocks until it completes.
(2) call swingworker.get with parameter to specify timeout. If there isn't a complete background tasks, block until it is completed - unless the timeout period, in this case, the get throws Java. Util. Concurrent. TimeoutException.
5. Tasks with intermediate results
It is useful to have a working background task provide intermediate results. The background task can call the swingworker.publish method to do this. This method takes many parameters. Each parameter must be one of the types specified by the second parameter of the SwingWorker.
You can override (override) swingworker.process to hold the results provided by the publish method. This method is called by the event dispatch thread. The result set from the publish method is typically collected by a process method.
Let's take a look at the example provided by filpper.java. Test java.util.Random by generating a set of Random Boolean values through a background task. It's like a coin toss experiment. To report its results, the background task USES an object, FlipPair.


private static class FlipPair {
 private final long heads, total;
 FlipPair(long heads, long total) {
  this.heads = heads;
  this.total = total;
 }
}

Heads is the result of true; Total represents the total number of flips.
A daemon is an instance of a FilpTask:
Private class FlipTask extends SwingWorker< Void, FlipPair> {
Since the task does not return a final result, there is no need to specify what the first type parameter is, use Void. After each "drop" the task invokes publish:


@Override
protected Void doInBackground() {
 long heads = 0;
 long total = 0;
 Random random = new Random();
 while (!isCancelled()) {
  total++;
  if (random.nextBoolean()) {
   heads++;
  }
  publish(new FlipPair(heads, total));
 }
 return null;
}

Because publish is called frequently, many of the FlipPair values are collected before the process method is called by the event dispatch thread; Process simply focuses on the last set of values returned each time and USES it to update the GUI:


protected void process(List pairs) {
 FlipPair pair = pairs.get(pairs.size() - 1);
 headsText.setText(String.format("%d", pair.heads));
 totalText.setText(String.format("%d", pair.total));
 devText.setText(String.format("%.10g",
   ((double) pair.heads)/((double) pair.total) - 0.5));
}

  6. Cancel background task
Call swingworker.cancel to cancel an executing background task. The task must be consistent with its own undo mechanism. There are two ways to do this:
(1) is terminated when an interrupt is received.
(2) call swingworker.iscanceled, which returns true if SwingWorker calls cancel.
  7. Bind properties and state methods
SwingWorker supports bound properties, which is useful when communicating with other threads. Two binding properties are provided: progress and state. Progress and state can be used to trigger event processing tasks in the event dispatch thread.
By implementing a property change listener, the program can catch changes to progress,state, or other binding properties.
 
7.1 The progress Bound Variable
The Progress binding variable is an integer variable that ranges from 0 to 100. It predefines the setter (the protected swingworker.setprogress) and the getter (the public swingworker.getprogress) methods.
 
7.2 The state Bound Variable
The change in the State binding variable reflects the change process of the SwingWorker object in its life cycle. This variable contains an enumerated type of swingworker.statevalue. Possible values are:
(1) PENDING
This state lasts as long as the doInBackground method is called from the creation of the object.
(2) STARTED
This state lasts from the moment before the doInBackground method is called to the moment before the done method is called.
(3) DONE
The object will remain in this state for the rest of its existence.
You need to return the value of the current state by calling swingworker.getstate.
 
7.3 the Status the Methods
Two methods provided by the Future interface can also report the status of background tasks. IsCancelled returns true if the task isCancelled. In addition, isDone returns true if the task is completed, either completed normally or canceled.


Using the top container

Swing provides three top-level container classes: JFrame, JDialog, and JApplet. When using these three classes, you must pay attention to the following:
 
(1). In order to be displayed on the screen, each GUI component must be part of the containment hierarchy. An containment hierarchy is a tree structure of a component, and the topmost container is its root.

(2). Each GUI component can only be included once. If a component is already in a container and an attempt is made to add it to a new container, the component is removed from the first container and added to the second.

(3). Each top-level container has a content pane, which typically contains (directly or indirectly) the visual components of all the top-level container guis.

(4). A menu bar can be added to the top container. Normally this menu bar is placed in the top container, but outside the content panel.

1. Top-level container and containment levels
Every program that USES Swing components has at least one top-level container. This top-level container is the root node that contains the hierarchy -- the hierarchy that contains all the Swing components that will appear in this top-level container.
      Typically, a single swgui-based application has at least one containment level, and its root node is a JFrame. For example, if an application has one window and two dialogs, the application will have three containment levels, or three top-level containers. An include hierarchy has a JFrame as its root node, and the other two include hierarchies each have a JDialog as its root node.
A small program (applet) based on Swing components has at least one containment level, and you can be sure that one of these has a JApplet as its root node. For example, if a small program has a dialog box, it will have two containment levels. The component in the browser window will be placed in an inclusion hierarchy, and its root node is a JApplet object. The dialog box will have an containment level, and its root node is a JDialog object.
 
2. Add components to the content panel
The following code action is to get the content panel of the frame and add the yellow label in the above example:
Frame. GetContentPane (). The add (yellowLabel, BorderLayout. CENTER);
 
As the code shows, you must first find the content panel of the top-level container, implemented by the method getContentPane. The default content panel is a simple intermediate container that inherits from JComponent and USES a BorderLayout as its panel manager.
Customizing a content panel is easy -- set the panel manager or add a border. It is important to note that the getContentPane method will return a Container object, not a JComponent object. This means that if you want to take advantage of some of the functionality of JComponent, you must also type the return value or create your own component as the content panel. Our example usually USES the second method, because the second method is clearer and clearer. Another approach we sometimes use is to simply add a self-defined component to the content panel, completely covering the content panel.
If you are creating your own content panel, make sure it is opaque. An opaque JPanel is a good choice. Note that JPanel layout management is FlowLayout by default, and you may want to replace it with another layout manager.
To make a component a content panel, you need to use the setContentPane method of the top container, for example:


//Create a panel and add components to it.
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(someBorder);
contentPane.add(someComponent, BorderLayout.CENTER);
contentPane.add(anotherComponent, BorderLayout.PAGE_END);
//Make it the content pane.
//contentPane.setOpaque(true);
topLevelContainer.setContentPane(contentPane);

 
Note: do not use a transparent container as a content pane, such as the JScrollPane, JSplitPane and the JTabbedPane.. a transparent panel will lead to the content of the components. Although you can make any transparent Swing components through setOpaque (true) method to make its lack of transparency, but when some components are set to completely opaque after don't look quite right. For example, a tag panel.
 
3. Adding a Menu Bar (Adding a Menu Bar)
In theory each top-level container can have a menu bar. But it turns a menu bar appear only in the Frame or Applet. To add a menu bar to a top-level container, you need to create a JMenuBar object, some assembly on the menu, then call setJMenuBar method. TopLevelDemo instance through the following code to add a menu bar into its Frame.


frame.setJMenuBar(cyanMenuBar);

 
4. The Root Pane
Each top-level container relies on an implicit intermediate container called the root container, which manages the content panel and the menu bar, along with two or more other containers (see Layered Pane, etc.). However, if you want to intercept mouse clicks or draw on multiple components, you need to know the root container.

Above already tells about the content pane with the content of the optional menu bar, here no longer repeat. The other two components contained within the root container, is layout panels and glass panels. Panels containing the menu bar and the content layout, and allows you to add other components to Z coordinate ordering. The glass panel is usually used to intercept occurs on the top floor of the input motions, and can also be used to drawing on multiple components.


Related articles: