Search code examples
javaswingswingworker

Java Swingworker one after another and update GUI


I am working on java swing with some multithreaded application. The situation is so, that I need to execute one thread after another i.e I output of one is related to the second. I did the following.

// Class one
public class Samp implements Runnable{
    public Samp(String name) {
       this.setName = name;
    }
    public void run (){
      // Here I have a file extraction method called
      extract(this.getName);
    }

   // Extract Method
   public Boolean extract(String filename){
     // Some extraction Operation.
     // I want to update the jTextField with the current filename under     extraction.
   }
}

// the GUI Class

public class Welcome extends javax.swing.JFrame {
   SwingWorker<Boolean, Void>worker =  new SwingWorker<Boolean, Void>() {
        @Override
        protected Boolean doInBackground() throws Exception {
            Thread t1 = new Thread(new ZipUtility(fileName));
            t1.start();
            return true;
        }

        // Can safely update the GUI from this method.
        @Override
        protected void done() {
            // Here when this worker is done I wanted to Run worker 2
        }
    };

    SwingWorker<Boolean, Void>worker2 = .........

   // Again when worker2 is done I wanted to run worker 3 and so on.    
 }

My Questions are :

1) How to run all the workers in a perfect sequencial order. i.e when worker1 is done then start worker 2, when worker2 is done then start worker 3 and so on.

2) How to update a textfield in my Welcome class, with the values from the Samp class.

Thanks in advance.


Solution

  • 1) How to run all the workers in a perfect sequencial order. i.e when worker1 is done then start worker 2, when worker2 is done then start worker 3 and so on.

    A little trick to remember, you can put a SwingWorker into a ExecutorService

    So, using something like...

    public static class Worker extends SwingWorker {
    
        private int sequence;
    
        public Worker(int sequence) {
            this.sequence = sequence;
        }
    
        @Override
        protected Object doInBackground() throws Exception {
            System.out.println(sequence + "...");
            Thread.sleep(500);
            return null;
        }
    
    }
    

    As a test, you could use something like...

    ExecutorService es = Executors.newFixedThreadPool(1);
    for (int index = 0; index < 10; index++) {
        es.submit(new Worker(index));
    }
    es.shutdown();
    

    (You don't need to call shutdown, but otherwise my tested never allowed the JVM to terminate ;))

    Which will run the workers in the order they are submitted.

    Now, if you want to feed the values from one SwingWorker to another, you could do something like...

    public abstract class ChainedWorker<T, V> extends SwingWorker<T, V> {
    
        private ChainedWorker<T, ?> next;
        private T previousValue;
    
        public ChainedWorker(ChainedWorker<T, ?> next) {
            this.next = next;
        }
    
        public void setPreviousValue(T previousValue) {
            this.previousValue = previousValue;
        }
    
        @Override
        protected void done() {
            try {
                T previous = get();
                if (next != null) {
                    next.setPreviousValue(previous);
                    next.execute();
                }
            } catch (InterruptedException | ExecutionException ex) {
                ex.printStackTrace();
            }
        }
    
    }
    

    Which is simply a SwingWorker which allows you to provide a link in the chain (the next worker to call), which passes the value that this SwingWorker generated...or some such similar

    Now, if you're really nuts and want to role your own, maybe something like...

    public class ChainedWorkerBuilder {
    
        private List<SwingWorker> workers;
        private SwingWorker current;
    
        public ChainedWorkerBuilder() {
            workers = new ArrayList<>(25);
        }
    
        public ChainedWorkerBuilder add(SwingWorker worker) {
            workers.add(worker);
            return this;
        }
    
        public void execute() {
            if (!workers.isEmpty()) {
                SwingWorker worker = workers.remove(0);
                worker.addPropertyChangeListener(new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        if ("state".equals(evt.getPropertyName())) {
                            SwingWorker source = (SwingWorker)evt.getSource();
                            switch (source.getState()) {
                                case DONE:
                                    source.removePropertyChangeListener(this);
                                    execute();
                                    break;
                            }
                        }
                    }
                });
            }
        }
    
    }
    

    2) How to update a textfield in my Welcome class, with the values from the Samp class.

    I don't think I'm following your code directly, but, if you did something like...

    SwingWorker<Boolean, Void>worker =  new SwingWorker<Boolean, String>() {
    
        protected Boolean doInBackground() throws Exception {
            publish(fileName);
            ZipUtility zu = new ZipUtility(fileName));
            return zu.extract(fileName);
        }
    

    Then in the SwingWorkers process method you would be able to update the UI safely...

        @Override
        protected void process(List<String> chunks) {
            // Grab the last element...
            textfield.setText(chunks.get(chunks.size() - 1));
        }
    }