SwingWorker exceptions lost even when using wrapper classes

后端 未结 3 1550
走了就别回头了
走了就别回头了 2021-02-02 02:11

I\'ve been struggling with the usability problem of SwingWorker eating any exceptions thrown in the background task, for example, described on this SO thread. That thread gives

相关标签:
3条回答
  • 2021-02-02 02:49

    The wrapper seems to works as expected. However, its implementation will never call done() if exception occurs. This is not suitable for many cases. It's probably simpler to call get() in done(). This will throw whatever exception that happened in doInBackground().

    Not sure how your example is structured, but it did not work in the application without EDT. So wrapping worker execution in SwingUtilities.invokeLater did help, ie:

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            new SpecialDataHelper().execute();
        }
    });
    

    The following example does print the exception stack trace:

    public class Tester {
    
        static class SpecialDataHelper extends SimpleSwingWorker {
            public SpecialDataHelper () {
            }
            public Void doInBackground() throws Exception {
                throw new Exception("test");
            }
            protected void done() {
            }
        }
    
        public static void main(String[] args) {
            try{
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        new SpecialDataHelper().execute();
                    }
                });
            } catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }
    

    Also consider this simple example that demonstrates how to get the exceptions that occurred in doInBackground() without using the wrapper. The wrapper is just a helper in case you forget to call get().

    import javax.swing.JOptionPane;
    import javax.swing.SwingUtilities;
    import javax.swing.SwingWorker;
    
    public class Tester {
        static class Worker extends SwingWorker<Void,Void> {
            @Override
            protected Void doInBackground() throws Exception {
                throw new Exception("test");
            }
            @Override
            protected void done() {
                try {
                    get();
                    JOptionPane.showMessageDialog(null, "Operation completed");
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(null, "Operation failed");
                } 
            }
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    new Worker().execute();
                }
            });         
        }
    }
    
    0 讨论(0)
  • 2021-02-02 02:51

    Forget about your wrapper, which eats the exceptions, whereas the SwingWorker doesn't. Here's how a SwingWorker should be used, and how you should handle a specific exception thrown from the background task:

    class MeaningOfLifeFinder extends SwingWorker<String, Object> {
        @Override
        public String doInBackground() throws SomeException {
            return findTheMeaningOfLife();
        }
    
        @Override
        protected void done() { // called in the EDT. You can update the GUI here, show error dialogs, etc.
            try { 
                String meaningOfLife = get(); // this line can throw InterruptedException or ExecutionException
                label.setText(meaningOfLife);
            } 
            catch (ExecutionException e) {
                Throwable cause = e.getCause(); // if SomeException was thrown by the background task, it's wrapped into the ExecutionException
                if (cause instanceof SomeException) {
                    // TODO handle SomeException as you want to
                }
                else { // the wrapped throwable is a runtime exception or an error
                    // TODO handle any other exception as you want to
                }
            }
            catch (InterruptedException ie) {
                // TODO handle the case where the background task was interrupted as you want to
            }
        }
    }
    
    0 讨论(0)
  • 2021-02-02 02:53

    I think everyone is making this too complicated. Just try this:

    String myResult="notSet";
    try {
        // from your example above
        helper.execute();  // this will call get+done on the actual worker
        myResult=helper.get();
    } catch (Exception e) {
    // this section will be invoked if your swingworker dies, and print out why...
         System.out.println("exception ");
         e.printStackTrace() ;
         myResult="Exception "+e.getMessage();
    }
    return myResult;
    

    The exceptions you throw but get eaten will be revealed. Two points will explain why this works. One, you only catch the remote exception from the calling thread whem you .get() the result. In more detail: To make the above example asynchronous, just move the .execute() higher up in the code. The moment you will discover the remote exception is after the asynch thread has bombed and you .get() the result. Two, by catching all Exceptions you will catch all special and subclassed exceptions that you may have built which the calling program may not have known about.

    0 讨论(0)
提交回复
热议问题