Multithreading issues with Swing around dialog cre

2019-01-29 14:07发布

问题:

Possible Duplicate:
SwingWorker in Java

I have several classes that need to work together but, they're not.

For one, I have Main:

public class Main
{

 public static void main(String[] args)
 {

  SwingUtilities.invokeLater(new Runnable()
  {

   @Override
   public void run()
   {

   //JFrame dummy for JDialog errors
   modal = new JFrame();

   new PrimaryErrorDialog("Title", "Message", e, false);

    JFrame masterWindow = new JFrame();
     masterWindow.setVisible();
   }
  }
 }
}

It creates PrimaryErrorDialog class:

private JDialog dialog = this;
private JTextArea text;
private JPanel buttonContainer;
private JButton sendErrorReportT, sendErrorReportF;

public PrimaryErrorDialog(String title, String message,
final Exception error, final boolean exit)
{
 super(Main.modal, title, true);

 //JButton for sending error report
 sendErrorReportT = new JButton("Send Error Report");
 sendErrorReportT.addActionListener(new ActionListener()
 {
  @Override
  public void actionPerformed(ActionEvent e)
  {
   dialog.dispose();

   dialog = new JDialog(Main.modal, "Sending...", true);

   Worker thread = new Worker(error);
   thread.start();

   JProgressBar progress = new JProgressBar();
   progress.setIndeterminate(true);

   dialog.add(progress);    
   dialog.pack();
   dialog.setVisible(true);
  }
 });

 //JButton for not sending error report
 sendErrorReportF = new JButton("Don't send error report");
 sendErrorReportF.addActionListener(new ActionListener()
 {
  @Override
  public void actionPerformed(ActionEvent e)
   {
    dialog.dispose();

    if (exit)
    {
     System.exit(0);
    }
   }
  });

 buttonContainer = new JPanel();
 buttonContainer.add(sendErrorReportT);
 buttonContainer.add(sendErrorReportF);

 add(text);
 add(buttonContainer);

 pack();
 setLocationRelativeTo(null);
 setVisible(true);
}

public void cleanUpAndContinue()
 {
  dialog.dispose();
  dialog = new JDialog(Main.modal, "title", true);
  dialog.add(/*Jbutton with listener that disposes this dialog*/)
  dialog.setVisible(true);
 }

It calls thw Worker class that extends Thread:

public class Worker extends Thread
{
    Exception e;

    public Worker(Exception error)
    {
        e = error;
    }

    @Override
    public void run()
    {
    //do something that takes time
     PrimaryErrorDialog.cleanUpAndContinue();
    }

It goes back to PrimaryErrorDialog, which would then have to inform the user that task was completed, and then terminate that dialog.

Then we go back to main where masterWindow is created.

All of this code is executed prior to creation of masterWindow, because this segment is ran when an error occurrs (if LAF is not present, .proprties files are missing, etc)...

That's why I created the dummy JFrame, so JDialog can attach to it, I didn't wan't to make it a JFrame.

This code is also executed later in program, for "real" runtime errors, some classes just have a bit different parameters and/or constructors.

However, this does not work, I have tried it in milion ways, i tried with SwingWorker, nothing seems to do what I want. Usually the email code is not even reached, or program doesn't wait for the dialogs to be disposed...

And what do I want?

Error occurrs. Dialog pops up telling me an error occurred and asking me if I want to send an error report. I say no - dialog closes, taking down the whole program if error is fatal. I say yes - dialog closes, new one opens with indetermined progressbar, WHILE email with stacktrace is being sent in the background. Email gets sent, dialog with progressbar closes and a new one opens telling my my error report was sent. I press ok and dialog closes, taking down the whole program if error is fatal, else, continues where it left of from Main class.

Note: An error can occurr in any class, it doesn't be from Main only...

回答1:

I've not read you're previous question, however...

The basic problem you have is needing to notify the dialog (which is within the EDT) that the background thread has completed (as well as provide feedback to the user if possible).

This is covered in the Concurrency in Swing lesson.

Below is a simple proof of concept. Personal, I would normally construct a custom panel and put it onto the a JDialog, but this just a proof of concept.

public class TestSwingWorker {

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {

                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                final JDialog dialog = new JDialog((Frame)null, "Happy, Happy, Joy, Joy", true);
                // This is so I can get my demo to work, you won't need it...
                dialog.addWindowListener(new WindowAdapter() {

                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(0);
                    }

                });

                JPanel panel = new JPanel(new GridBagLayout());
                panel.setBorder(new EmptyBorder(8, 8, 8, 8));
                dialog.setContentPane(panel);

                // You'll need you own icon...
                JLabel lblIcon = new JLabel(new ImageIcon(getClass().getResource("/waiting-64.png")));
                JLabel lblMessage = new JLabel("<html>Hard and work here<br>Please wait...</html>");
                final JProgressBar progressBar = new JProgressBar();

                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 0;
                gbc.gridy = 0;
                gbc.anchor = GridBagConstraints.NORTH;
                gbc.weighty = 1;
                gbc.insets = new Insets(2, 2, 2, 2);
                gbc.gridheight = GridBagConstraints.REMAINDER;
                dialog.add(lblIcon, gbc);

                gbc.gridheight = 1;
                gbc.gridx = 1;
                gbc.gridy = 0;
                gbc.anchor = GridBagConstraints.WEST;
                gbc.weighty = 0;
                gbc.weightx = 1;
                dialog.add(lblMessage, gbc);

                gbc.gridy = 1;
                gbc.anchor = GridBagConstraints.NORTH;
                gbc.weighty = 1;
                dialog.add(progressBar, gbc);

                dialog.pack();
                dialog.setLocationRelativeTo(null);

                MyWorker worker = new MyWorker();
                // Get notification back from the worker...
                worker.addPropertyChangeListener(new PropertyChangeListener() {

                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {

                        MyWorker worker = (MyWorker) evt.getSource();
                        // Progress has been updated
                        if (evt.getPropertyName().equalsIgnoreCase("progress")) {

                            progressBar.setValue((Integer)evt.getNewValue());

                        // The state of the worker has changed...
                        } else if (evt.getPropertyName().equalsIgnoreCase("state")) {

                            if (worker.getState().equals(SwingWorker.StateValue.DONE)) {

                                dialog.dispose();

                            }

                        }

                    }
                });

                worker.execute();

                dialog.setVisible(true);

            }

        });

    }

    protected static class MyWorker extends SwingWorker<Object, Object> {

        @Override
        protected Object doInBackground() throws Exception {

            // My long running task...
            for (int index = 0; index < 100; index++) {

                // Change this to make it faster or slower...
                Thread.sleep(250);
                setProgress(index);

            }

            return null;

        }

    }

}


回答2:

  • dirty hack wrap dialog.setVisible(true); into invokeLater()

  • use SwingWorker or Runnable#Thread instead or plain Thread

  • is there issue that you ignored answers and especially comments from your previous question

  • Swing Gui doesn't care about output from Thread, because there isn't implemented notifiers for Event Dispatch Thread, you have to invoke that by invokeLater, or to use SwingWorker,

  • SwingWorkers methods done, publish and progress can notify EDT correctly