How can i catch Event Dispatch Thread (EDT) exceptions?

后端 未结 3 955
北海茫月
北海茫月 2020-11-30 06:37

I am using a class called MyExceptionHandler that implements Thread.UncaughtExceptionHandler to handle normal exceptions in my project.

As

相关标签:
3条回答
  • 2020-11-30 07:14

    The EDT exception handler doesn't use Thread.UncaughtExceptionHandler. Instead, it calls a method with the following signature:

    public void handle(Throwable thrown);
    

    Add that to MyExceptionHandler, and it should work.

    The "documentation" for this is found in EventDispatchThread, which is a package-private class in java.awt. Quoting from the javadoc for handleException() there:

    /**
     * Handles an exception thrown in the event-dispatch thread.
     *
     * <p> If the system property "sun.awt.exception.handler" is defined, then
     * when this method is invoked it will attempt to do the following:
     *
     * <ol>
     * <li> Load the class named by the value of that property, using the
     *      current thread's context class loader,
     * <li> Instantiate that class using its zero-argument constructor,
     * <li> Find the resulting handler object's <tt>public void handle</tt>
     *      method, which should take a single argument of type
     *      <tt>Throwable</tt>, and
     * <li> Invoke the handler's <tt>handle</tt> method, passing it the
     *      <tt>thrown</tt> argument that was passed to this method.
     * </ol>
     *
     * If any of the first three steps fail then this method will return
     * <tt>false</tt> and all following invocations of this method will return
     * <tt>false</tt> immediately.  An exception thrown by the handler object's
     * <tt>handle</tt> will be caught, and will cause this method to return
     * <tt>false</tt>.  If the handler's <tt>handle</tt> method is successfully
     * invoked, then this method will return <tt>true</tt>.  This method will
     * never throw any sort of exception.
     *
     * <p> <i>Note:</i> This method is a temporary hack to work around the
     * absence of a real API that provides the ability to replace the
     * event-dispatch thread.  The magic "sun.awt.exception.handler" property
     * <i>will be removed</i> in a future release.
     */
    

    How exactly Sun expected you find this, I have no idea.

    Here's a complete example which catches exceptions both on and off the EDT:

    import javax.swing.SwingUtilities;
    
    public class Test {
      public static class ExceptionHandler
                                       implements Thread.UncaughtExceptionHandler {
    
        public void handle(Throwable thrown) {
          // for EDT exceptions
          handleException(Thread.currentThread().getName(), thrown);
        }
    
        public void uncaughtException(Thread thread, Throwable thrown) {
          // for other uncaught exceptions
          handleException(thread.getName(), thrown);
        }
    
        protected void handleException(String tname, Throwable thrown) {
          System.err.println("Exception on " + tname);
          thrown.printStackTrace();
        }
      }
    
      public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
        System.setProperty("sun.awt.exception.handler",
                           ExceptionHandler.class.getName());
    
        // cause an exception on the EDT
        SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            ((Object) null).toString();        
          }
        });
    
        // cause an exception off the EDT
        ((Object) null).toString();
      }
    }
    

    That should do it.

    0 讨论(0)
  • 2020-11-30 07:22

    Just for some extra info, in many cases Throwables can get caught by the EDT's UncaughtExceptionHandler even in 1.5 and 1.6. Looking at the source code for EventDispatchThread in 1.5.0_22:

    private void processException(Throwable e, boolean isModal) {
        if (!handleException(e)) {
            // See bug ID 4499199.
            // If we are in a modal dialog, we cannot throw
            // an exception for the ThreadGroup to handle (as added
            // in RFE 4063022).  If we did, the message pump of
            // the modal dialog would be interrupted.
            // We instead choose to handle the exception ourselves.
            // It may be useful to add either a runtime flag or API
            // later if someone would like to instead dispose the
            // dialog and allow the thread group to handle it.
            if (isModal) {
                System.err.println(
                    "Exception occurred during event dispatching:");
                e.printStackTrace();
            } else if (e instanceof RuntimeException) {
                throw (RuntimeException)e;
            } else if (e instanceof Error) {
                throw (Error)e;
            }
        }
    }
    
    private boolean handleException(Throwable thrown) {
    
        try {
    
            if (handlerClassName == NO_HANDLER) {
                return false;   /* Already tried, and failed */
            }
    
            /* Look up the class name */
            if (handlerClassName == null) {
                handlerClassName = ((String) AccessController.doPrivileged(
                    new GetPropertyAction(handlerPropName)));
                if (handlerClassName == null) {
                    handlerClassName = NO_HANDLER; /* Do not try this again */
                    return false;
                }
            }
    
            /* Load the class, instantiate it, and find its handle method */
            Method m;
            Object h;
            try {
                ClassLoader cl = Thread.currentThread().getContextClassLoader();
                Class c = Class.forName(handlerClassName, true, cl);
                m = c.getMethod("handle", new Class[] { Throwable.class });
                h = c.newInstance();
            } catch (Throwable x) {
                handlerClassName = NO_HANDLER; /* Do not try this again */
                return false;
            }
    
            /* Finally, invoke the handler */
            m.invoke(h, new Object[] { thrown });
    
        } catch (Throwable x) {
            return false;
        }
    
        return true;
    }
    

    According to this code, there are only 3 ways a Throwable will not be caught by the EDT Thread's UncaughtExceptionHandler:

    1. The Throwable is handled by the sun.awt.exception.handler successfully (the class was found, instantiated and its handle(Throwable) method called without throwing anything)
    2. The EDT is in a modal dialog
    3. The Throwable is neither a RuntimeException nor an Error
    0 讨论(0)
  • 2020-11-30 07:22

    Summarizing the above... with newer Java you can just do this:

    // Log exceptions thrown on the event dispatcher thread
    SwingUtilities.invokeLater(()
      -> Thread.currentThread().setUncaughtExceptionHandler((thread, t)
      -> this.log.error("exception in event dispatcher thread", t)));
    
    0 讨论(0)
提交回复
热议问题