Подтвердить что ты не робот

Как можно исключить исключения Event Dispatch Thread (EDT)?

Я использую класс под названием MyExceptionHandler, который реализует Thread.UncaughtExceptionHandler для обработки нормальных исключений в моем проекте.

Как я понимаю, этот класс не может поймать исключения EDT, поэтому я попытался использовать это в методе main() для обработки исключений EDT:

public static void main( final String[] args ) {
    Thread.setDefaultUncaughtExceptionHandler( new MyExceptionHandler() );  // Handle normal exceptions
    System.setProperty( "sun.awt.exception.handler",MyExceptionHandler.class.getName());  // Handle EDT exceptions
    SwingUtilities.invokeLater(new Runnable() {  // Execute some code in the EDT. 
        public void run() {
            JFrame myFrame = new JFrame();
             myFrame.setVisible( true );
        }
    });
}

Но до сих пор он не работает. Например, при инициализации JFrame я загружаю его метки из файла пакета в конструкторе следующим образом:

setTitle( bundle.getString( "MyJFrame.title" ) );

Я удалил ключ MyJFrame.title из файла пакета для проверки обработчика исключений, но он не сработает! Исключение обычно печаталось в журнале.

Я делаю что-то не так здесь?

4b9b3361

Ответ 1

Обработчик исключений EDT не использует Thread.UncaughtExceptionHandler. Вместо этого он вызывает метод со следующей сигнатурой:

public void handle(Throwable thrown);

Добавьте это к MyExceptionHandler, и он должен работать.

"Документация" для этого находится в EventDispatchThread, который является частным классом пакета в java.awt. Цитата из javadoc для handleException() там:

/**
 * 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 context class loader,
 * <li> Instantiate that class using its zero-argument constructor,
 * <li> Find the resulting handler object <tt>public void handle</tt>
 *      method, which should take a single argument of type
 *      <tt>Throwable</tt>, and
 * <li> Invoke the handler <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 <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.
 */

Как точно Sun ожидало, что вы найдете это, я понятия не имею.

Вот полный пример, который ловит исключения как на 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();
  }
}

Это должно сделать это.

Ответ 2

Просто для дополнительной информации, во многих случаях Throwables могут попасть в EDT UncaughtExceptionHandler даже в 1.5 и 1.6. Посмотрите исходный код EventDispatchThread в 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;
}

В соответствии с этим кодом существует только 3 способа: Throwable не будет поймать EDT Thread UncaughtExceptionHandler:

  • The Throwable обрабатывается sun.awt.exception.handler успешно (класс был найден, создан экземпляр и метод дескриптора (Throwable), вызываемый без метания)
  • EDT находится в модальном диалоге
  • The Throwable не является ни RuntimeException, ни Error

Ответ 3

Подводя итог выше... с более новой Java, вы можете просто сделать это:

// Log exceptions thrown on the event dispatcher thread
SwingUtilities.invokeLater(()
  -> Thread.currentThread().setUncaughtExceptionHandler((thread, t)
  -> this.log.error("exception in event dispatcher thread", t)));