1

First, I need to inform you that I am trying my hardest to learn how to code in Java. Its been a little bit difficult, but, I do believe I have it. I have submitted a couple question in the past in regards to SwingWorkers and the like. Each of which I thought I had it, but come to find out that I still had learning to do. Hopefully this time, is not one of those times.

With that said, please let me know of anything you see that isn't to standard, or, could lead to problems in the future.


Now for the question!

I have constructed a JFrame, of which, loads a couple things before it allows the user to proceed to another page, press buttons, or anything else. Once it loads the data it will then unlock the JFrame to allow the user to interact with the data.

The problem is, (which isn't really a problem, just a clarification) I need to execute another task while the user can interact with the JFrame in such a way it doesn't bother them, but, will update the JFrame based on the results it found. An example might be a version check. Depending if the version is out of date or not, notify user.


Example Sudo Code

protected void startJFrame() {
    JFrame myFrame = new JFrame();//Starts with disable/invisible components. Preventing the user from doing to much before allowed.
    SwingWorker<Void, Progress> loadingWorker = new SwingWorker<Void, Progress>() {
        @Override
        protected Void doInBackground() throws Exception {
            publish(new Progress(0,"Loading This"));      // Set Percent
            loadingTasks.loadThis();                         // Do Work!!
            publish(new Progress(25,"Loading That"));     // Set Percent
            loadingTasks.loadThat();                         // Do Work!!
            publish(new Progress(50,"Loading More"));     // Set Percent
            loadingTasks.loadMore();                         // Do Work!!
            publish(new Progress(75,"Loading Last"));     // Set Percent
            loadingTasks.loadLast();                         // Do Work!!
            publish(new Progress(100,"Loading Complete"));// Set Percent
            return null;
        }
        @Override
        protected void process(List<Progress> ProgressList) {
            for (Progress p : ProgressList) {
                System.out.println(p.getInt() + "% " + p.getString()); //Show user percent and what its doing.
            }
        }
        @Override
        protected void done() {
            try {
                get();
                loadingTasks.WrapUp();//Set Variables or other small stuff.
                myFrame.userAllowed();//Lets the user interact with the whole JFrame.
                SwingWorker<Void, Void> secondWorker = new SwingWorker<Void, Void>() {
                    @Override
                    protected Void doInBackground() throws Exception {
                        versionCheck.makeItSo();// Do Work!!
                        return null;
                    }
                    @Override
                    protected void done() {
                        try {
                            get();
                            versionCheck.wrapUp();//Set Variables or other small stuff.
                            myFrame.showLabel();//Show a label with specific info.
                        } catch (InterruptedException | ExecutionException e) {
                            e.printStackTrace();
                        }
                    }
                };
                secondWorker.execute();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    };
    loadingWorker.execute();
}

Off topic question involving this code.

I am concerned about creating to many objects and not disposing of them, just to pass multiple variables. Specifically the Progress objects being created in the first doInBackground method.

Is it considered OK to do this? Will it auto dispose the Progress Objects automatically? If not, how would i go about disposing them after I'm done with them?

TrinaryAtom
  • 242
  • 3
  • 14
  • 1
    Can both tasks running simultaneously? Do they need to run one after the other. Java's garbage collection is very good at short lived objects, beweary, but it shouldn't be a huge problem... – MadProgrammer Apr 17 '14 at 06:02
  • @MadProgrammer No, Yes. When the first task completes it creates an object that would be other wise inaccessible. Which means that if something goes wrong in the first task it doesn't create that object. The "versionCheck" would need access to that object. – TrinaryAtom Apr 17 '14 at 06:08
  • I realize that they can exist within the same swingworker. And, that's how it was originally built. But, I am striving for a solution like this because it works perfectly for my situation. – TrinaryAtom Apr 17 '14 at 06:15
  • You create extensions of the `SwingWorker` so all you would need to do is `new LoadingWorker().execute()`... – MadProgrammer Apr 17 '14 at 06:16
  • @MadProgrammer Do you mean in the `done` method? In that situation would I pass reference of `myFrame` in a variable? – TrinaryAtom Apr 17 '14 at 06:19
  • I'm thinking of putting the `myFrame` into a static variable then accessing it from within the version _class with the extension_. Also, i'm just striving for completeness. Making sure I'm not violating any rules in the Java code/community. – TrinaryAtom Apr 17 '14 at 06:41
  • 1
    `static` is not your friend. I've done an answer, I "hope" it gives you some ideas. At some point you are going to need to pass something to the `SwingWorker`, you could pass a reference of the frame, but I don't like doing this, gives to much power to the caller to do things it shouldn't, I prefer to use an interface of some kind so I can dictate the contract between the two. A better solution might be to just use a `PropertyChangeListener`... – MadProgrammer Apr 17 '14 at 06:52

1 Answers1

3

SwingWorker supports PropertyChange events, that is, you can listener to when the SwingWorker changes state or updates it's progress...yes, SwingWorker even supports progress notification, for example

This means you could set up a PropertyChangeListener to monitor changes to the progress and state properties and take appropriate actions...

A worker that simple sets progress updates...

public class LoadMaster extends SwingWorker<Void, Progress> {

    @Override
    protected Void doInBackground() throws Exception {
        System.out.println("Working hard here, nothing to see...");
        for (int index = 0; index < 100; index++) {
            Thread.sleep(10);
            setProgress(index);
        }
        return null;
    }

    @Override
    protected void done() {
        try {
            get();
        } catch (Exception e) {
        }
    }

}

A example PropertyChangeListener...

public class LoadMasterPropertyChanegHandler implements PropertyChangeListener {

    private SwingWorkerExample example;

    public LoadMasterPropertyChanegHandler(SwingWorkerExample example) {
        this.example = example;
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        System.out.println(evt.getPropertyName());
        if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
            int value = (int) evt.getNewValue();
            example.showProgress(value);
        } else if ("state".equalsIgnoreCase(evt.getPropertyName())) {
            SwingWorker worker = (SwingWorker) evt.getSource();
            if (worker.isDone()) {
                try {
                    worker.get();
                    example.loadCompleted();
                } catch (InterruptedException | ExecutionException exp) {
                    example.loadFailed();
                }
            }
        }
    }

}

Now, all this does is sends back information to the SwingWorkerExample (it's coming) which allows it to determine what it should do...

In this example, the loadCompleted method updates the UI and then starts the second worker...

protected void loadCompleted() {
    //...
    LoadStuffWorker stuffWorker = new LoadStuffWorker(this);
    stuffWorker.execute();
}

In all truth, I might use interfaces instead, so I'm not overtly exposing the class, but that's a topic for another day...

And the full example...

Worker

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class SwingWorkerExample {

    private JProgressBar pb;
    private JPanel content;

    public static void main(String[] args) {

        new SwingWorkerExample();

    }

    public SwingWorkerExample() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                pb = new JProgressBar();
                content = new JPanel();
                content.setBorder(new EmptyBorder(10, 10, 10, 10));

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setContentPane(content);
                frame.setLayout(new GridBagLayout());
                frame.add(pb);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                LoadMaster master = new LoadMaster();
                master.addPropertyChangeListener(new LoadMasterPropertyChanegHandler(SwingWorkerExample.this));
                master.execute();
            }
        });
    }

    protected void showProgress(int value) {
        pb.setValue(value);
    }

    protected void loadCompleted() {
        content.removeAll();
        content.setLayout(new GridLayout(0, 1));
        content.add(new JLabel("All your base are belong to us"));
        content.revalidate();

        LoadStuffWorker stuffWorker = new LoadStuffWorker(this);
        stuffWorker.execute();
    }

    protected void loadFailed() {
        content.removeAll();
        content.setLayout(new GridLayout(0, 1));
        content.add(new JLabel("Fail"));
        content.revalidate();
    }

    protected void setVersion(String value) {
        content.add(new JLabel("Version: " + value));
        content.revalidate();
    }

    protected void failed(String fail) {
        content.add(new JLabel(fail));
        content.revalidate();
    }

    public class LoadMaster extends SwingWorker<Void, Progress> {

        @Override
        protected Void doInBackground() throws Exception {
            System.out.println("Working hard here, nothing to see...");
            for (int index = 0; index < 100; index++) {
                Thread.sleep(10);
                setProgress(index);
            }
            return null;
        }

        @Override
        protected void done() {
            try {
                get();
            } catch (Exception e) {
            }
        }

    }

    public class LoadStuffWorker extends SwingWorker<String, Void> {

        private SwingWorkerExample example;

        public LoadStuffWorker(SwingWorkerExample example) {
            this.example = example;
        }

        @Override
        protected String doInBackground() throws Exception {
            System.out.println("Hanging about in the background");
            Thread.sleep(3000);
            return "Hello from the dark side";
        }

        @Override
        protected void done() {
            try {
                String value = get();
                example.setVersion(value);
            } catch (InterruptedException | ExecutionException ex) {
                example.failed("Fail while doing version check");
            }
        }

    }

    public class Progress {
    }

    public class LoadMasterPropertyChanegHandler implements PropertyChangeListener {

        private SwingWorkerExample example;

        public LoadMasterPropertyChanegHandler(SwingWorkerExample example) {
            this.example = example;
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            System.out.println(evt.getPropertyName());
            if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
                int value = (int) evt.getNewValue();
                example.showProgress(value);
            } else if ("state".equalsIgnoreCase(evt.getPropertyName())) {
                SwingWorker worker = (SwingWorker) evt.getSource();
                if (worker.isDone()) {
                    try {
                        worker.get();
                        example.loadCompleted();
                    } catch (InterruptedException | ExecutionException exp) {
                        example.loadFailed();
                    }
                }
            }
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • O.O Where is the `more than I asked for upvote button of supreme awesome`. This is a totally exceptional answer! Thank you! – TrinaryAtom Apr 17 '14 at 06:59
  • @TrinaryAtom thought I was off topic :P – MadProgrammer Apr 17 '14 at 07:01
  • After going through your example code, absorbing and learning it, I have one question. You have `get();` in `done()` and `propertyChange()` of the same worker. Im guessing its ok to call `get()` as many times as you want? Either way, would it be ok to just remove the `done()` in `LoadMaster`? Since `get()` is already being called in the `propertyChange()`. – TrinaryAtom Apr 17 '14 at 07:29
  • 1
    If you don't need it, know yourself out. And yes, you can call `get` as much you want... – MadProgrammer Apr 17 '14 at 07:31
  • Just checking, making sure there wasn't something I was missing and once again thank you so much. Your awesome! – TrinaryAtom Apr 17 '14 at 07:38