Hidden Java 7 Features – SecondaryLoop

I had planned on continuing posting about “Hidden Java 7 Features” much earlier, but spent too much time on working examples that were too elaborate. Such was the case with SecondaryLoop, so I decided to go ahead and just talk about it now and perhaps post my example later (it’s a cool visualization of hard drive contents).

java.awt.SecondaryLoop is a new Java 7 interface that enables improved control of the execution flow of a Swing/AWT program. I’m labeling it a “hidden” feature in the sense that there’s not much written about it (at least that I could find on Google) beyond its javadoc description. It’s the type of feature you won’t see in a “top ten” list of Java 7 enhancements, but if you’re doing Java UI work it’s a great addition that will simplify your code.

So what does it do?

Often in your UI program, your next step depends on some other task; input from the user, a network call, reading a file, etc. Being event driven, the flow of a UI application can get more complex in these situations, specially when these tasks could be long running and you don’t want to hang the UI. Remember, doing any task that takes a long time (like I/O) on the event dispatcher thread will hang your UI. The typical solution is to then use a SwingWorker for that code. The problem with SwingWorker is that it makes the code a bit more complicated. Once you do work in the worker, you have to call another method to perform the next task in the UI. Let’s look at this following diagram.

The blue boxes represent tasks that would have to be done in a SwingWorker. The flow of execution then is handed off to the worker, when the first worker task is done (Find all files), it then has to call “build treemap” when it is done. That worker will then do its work, and when done it will call “draw treemap” (which doesn’t need to be executed in a SwingWorker, here it just shows that it would have to be called again).

The alternative is to use SecondaryLoops. We can pretty much write the main flow of the code more linearly, as shown by the green boxes. Every time we have a task that can take a long time, we can then use SecondaryLoop to do the work in a thread but hold the flow of execution without blocking the UI until that thread is done with its work. This is how this code looks like …

    public void start() {
        startStopButton.setText("Cancel");
        initializeFileTypeDatabase();

        JFileChooser fileChooser = new JFileChooser();
        fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        fileChooser.showOpenDialog(this);

        List files = findFiles(fileChooser.getSelectedFile());

        if (working) {
            FileAnalysis analysis = analyzeFiles(files);

            treemap.buildTreemap((List)analysis.getCategories());
        }
        progressText.setText("");

        working = false;

        startStopButton.setEnabled(true);
        startStopButton.setText("Start");
    }

The code is:

  • Asking the user for a top-level folder/directory
  • Gets all files under that directory (block without freezing UI)
  • Builds treemap (block without freezing UI)

Let’s look at the SecondaryLoop usage of the findFiles method.

 /**
 * Given a root file/directory, return all files underneath it.
 * This method can be safely invoked from the UI dispatch 
 * thread without blocking the UI
 */
 public List findFiles(final File baseDir) {
        working = true;
        final List files = new ArrayList<>();

        if (baseDir == null || !baseDir.isDirectory()) {
            System.out.println("Not a directory!");
            return null;
        }

        if (baseDir != null && baseDir.exists() &&
            baseDir.isDirectory()) {
            Toolkit kit = Toolkit.getDefaultToolkit();

            // Create secondary loop from awt Toolkit, this is the object
            // we'll use to block the flow of code without freezing the UI
            // until another task/thread finishes its work
            final SecondaryLoop loop =
              kit.getSystemEventQueue().createSecondaryLoop();

            Thread work = new Thread() {

                public void run() {
                    getFiles(baseDir, files);
                    loop.exit();
                }
            };

            // We start the thread to do the real work
            work.start();

            // Blocks until loop.exit() is called
            loop.enter();
        }

        return files;
    }
    /**
    * Recursively traverse all children of "root" 
    * and store them in "files"
    */
    public void getFiles(File root, List files) {
        if (root.isDirectory() && working) {
            File[] children = root.listFiles();

            if (children != null) {
                for (File file : children) {
                    if (file.isDirectory()) {
                        getFiles(file, files);
                    } else {
                        files.add(file);
                        status(file.getAbsolutePath());
                    }

                    if (!working) {
                        break;
                    }
                }
            }
        }
    }

So findFiles() is a method that will return all files under a given base directory. This method will gather all the results and come back when done, without freezing the UI even when called from the event dispatch thread. The nice thing about it is that then our code can flow more naturally. As we see in the previous code listing, start is a method called after a button pressed and we can call sequences of methods that do a lot of work, while keeping the main UI code nice and simple. When we do this with SwingWorkers, the flow of execution gets more complicated, because once the SwingWorker is done, if there are more tasks to be performed we have to called another method.

Another great thing about this method is that we can change the UI state directly after actions are performed. At the beginning of the start() method we change the name of the startStopButton to “Cancel”, then at the end we reset it back to “Start”. We can then write cleaner UI methods with state changes (like enabled, hidden, etc) in one method, instead of doing this at the end of our last SwingWorker, coordinating across different SwingWorkers and/or methods.

In conclusion, SecondaryLoop is not very difficult to use and is great to simplify your Java UI code and make your life happier! :-)

You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

8 Comments »

 
  • [...] Hidden Java 7 Features – SecondaryLoop… - sellmic.com [...]

  • I want to write a java application for remote [administration] screen capture. For this I need to run my java application during startup and in a hidden mode in which users must not be able to see or stop it. I use console to execute java programs. Help me with making the application start at system startup and stealth mode.

  • Thanks for your time with this example of Java secondary loops. I just converted several Threaded SwingWorkers I had on my latest project to SL's. The lineraity of the implematation makes for much easier to follow and less complicated code. Thanks again – Chuck

  • we are wroting the following method….

    public void printPattern(int n) {
    for (int i=1; i<=n; i++) {
    for(int j=0; j<i; j++) {
    System.out.print((i+j)%2);
    }
    System.out.println();
    }
    }
    thanx…to all

  • it well be posible print the multiplication table using for loop in java

  • you can veiw A for loop has the test at the start of the loop and also has a built in subscript as part of the loop.
    The loop is done zero or more times. thanx to all

  • I have some more Ideas about more features that I think could be added to Java. We are working on a project in our University about the advanced Java features and I hope we will get some joyful results.

  • JavaNoob says:

    Hi,
    How is it possible to queue the secondary loop actions to make sure that the behaviour is not getting chaotic if somebody for example clicks many times ? (acc. to javadoc I understand that .enter() should do it but in my example, it does not work)
    As example:
    I adapted your code to the following example http://www.0×13.de/index.php/code-snippets/51-swi….
    I added a JLabel in the loop to display the submitted integer. Now I have the following pb:
    1st I submit 20000 which takes >30sec to calculate.
    After I immediately submit 10 which takes <1sec to calculate. Unfortunately when I do that the Jlabel displays the new submitted value without waiting for the first loop to end.

 

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>