如何捕获事件调度线程 (EDT) 异常?

发布于 2024-10-07 20:48:34 字数 930 浏览 3 评论 0原文

我正在使用一个名为 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 以测试异常处理程序,但它不起作用!异常通常会打印在日志中。

我在这里做错了什么吗?

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

As I understand this class can't catch the EDT exceptions, so I tried to use this in the main() method to handle EDT exceptions:

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 );
        }
    });
}

But untill now it's not working. For example while initializing a JFrame I load its labels from a bundle file in the constructor like this:

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

I deleted the key MyJFrame.title from the bundle file to test the exception handler, but it didn't work! The exception was normally printed in the log.

Am I doing something wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

风筝在阴天搁浅。 2024-10-14 20:48:34

EDT 异常处理程序不使用 Thread.UncaughtExceptionHandler。相反,它调用具有以下签名的方法:

public void handle(Throwable thrown);

将其添加到 MyExceptionHandler,它应该可以工作。

有关此内容的“文档”可在 EventDispatchThread 中找到,它是 java.awt 中的包私有类。引用 handleException() 的 javadoc:

/**
 * 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.
 */

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();
  }
}

应该可以。

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.

許願樹丅啲祈禱 2024-10-14 20:48:34

总结以上内容...使用较新的 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)));

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)));
一念一轮回 2024-10-14 20:48:34

仅提供一些额外信息,在许多情况下,即使在 1.5 和 1.6 中,Throwables 也可能被 EDT 的 UncaughtExceptionHandler 捕获。查看1.5.0_22中EventDispatchThread的源代码:

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线程的UncaughtExceptionHandler捕获:

  1. Throwable被sun.awt.exception.handler成功处理(找到类,实例化并调用其句柄(Throwable)方法,但不抛出任何内容)
  2. EDT 位于模式对话框中
  3. Throwable 既不是 RuntimeException 也不是错误

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文