Search code examples
javaswingconcurrencyevent-dispatch-threadjprogressbar

Java uploading percentage progressbar thread


I'm developing a java application where I save big files using bufferedInputStream. I have put a progressbar inside a JDialog which indicates the percentage of uploaded files and which increases every n seconds. The problem is that the application waits indefinitely for the dialog to close and so it never exits. Anyone can help?

here is the main application snippet:

JDialog dialog = new JDialog(Main.getMainFrame(), true);
                    ProgressBarJPanel progressBarJPanel = new ProgressBarJPanel();
                    dialog.setContentPane(progressBarJPanel);
                    dialog.pack();
                    dialog.setVisible(true);
                    while ((val = bufferedInputStream.read()) != -1)
                    {
                        fileOutputStream.write(val);
                    }
                    progressBarJPanel.end();
                    dialog.setVisible(false);

and the incriminated class

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ProgressBarJPanel extends JPanel
        implements ActionListener
{
    private JProgressBar progressBar;
    private Timer timer;
    public Thread updateThread;
    public final static int ONE_SECOND = 1000;
    private JTextArea taskOutput;
    private String newline = "\n";
    int timeNow= 0;
    int progress = 0;

    public ProgressBarJPanel()
    {
        super(new BorderLayout());

        progressBar = new JProgressBar(0, 100);
        progressBar.setValue(0);
        progressBar.setStringPainted(true);
        taskOutput = new JTextArea(5, 20);
        taskOutput.setMargin(new Insets(5,5,5,5));
        taskOutput.setEditable(false);
        taskOutput.setCursor(null);

        JPanel panel = new JPanel();
        panel.add(progressBar);

        add(panel, BorderLayout.PAGE_START);
        add(new JScrollPane(taskOutput), BorderLayout.CENTER);
        setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

        //Create a timer.
        timer = new Timer(ONE_SECOND, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
                progressBar.setValue(progress);
                progress+=10;
                String s = "now at "+progress+"%";
                if (s != null) {
                    taskOutput.append(s + newline);
                    taskOutput.setCaretPosition(
                            taskOutput.getDocument().getLength());
                }
            }
        });

    }

    public void end()
    {
        timer.stop();
    }

    public void startProgress()
    {
        timer.start();
    }

    /**
     * Create the GUI and show it. For thread safety, this method should be
     * invoked from the event-dispatching thread.
     */
    private static void createAndShowGUI()
    {
        //Make sure we have nice window decorations.
        JFrame.setDefaultLookAndFeelDecorated(true);

        //Create and set up the window.
        JFrame frame = new JFrame("ProgressBarDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        JComponent newContentPane = new ProgressBarJPanel();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args)
    {
                createAndShowGUI();
    }

    @Override
    public void actionPerformed(ActionEvent e)
    {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}

-- EDIT

Here is the solution based on ben75 advice:

JDialog dialog = new JDialog(Main.getMainFrame(), true);
ProgressBarJPanel progressBarJPanel = new ProgressBarJPanel();
dialog.setContentPane(progressBarJPanel);
Runnable r = new Runnable(){
  public void run(){
             SwingUtilities.invokeLater(new Runnable()
             {
                    @Override
                    public void run()
                    {
                       progressBarJPanel.startProgress();
                       dialog.pack();
                       dialog.setVisible(true);
                    }
             });

            //this is the long running job
            while ((val = bufferedInputStream.read()) != -1)
            {
                fileOutputStream.write(val);
            }

            //here is the callback to UI thread
            SwingUtilities.invokeLater(new Runnable(){
                public void run(){
                    progressBarJPanel.end();
                    dialog.setVisible(false);
                }
           }
  };
Thread t = new Thread(r);
t.start();

Solution

  • You must do time consumming jobs in another thread (i.e. non UI thread) and at the end of the job callback the UI thread to close the dialog.

    More or less how it can be coded:

    JDialog dialog = new JDialog(Main.getMainFrame(), true);
    ProgressBarJPanel progressBarJPanel = new ProgressBarJPanel();
    dialog.setContentPane(progressBarJPanel);
    dialog.pack();
    dialog.setVisible(true);
    Runnable r = new Runnable(){
          public void run(){
                    //this is the long running job
                    while ((val = bufferedInputStream.read()) != -1)
                    {
                        fileOutputStream.write(val);
                    }
                    //here is the callback to UI thread
                    SwingUtilities.invokeLater(new Runnable(){
                        public void run(){
                            progressBarJPanel.end();
                            dialog.setVisible(false);
                        }
                   }
          };
    Thread t = new Thread(r);
    t.start();