使用SwingWorker时如何处理异常?

时间:2009-04-26 17:16:05

标签: java swing swingworker

我在Java 6中使用SwingWorker来避免在事件派发线程上运行长时间运行的代码。

如果在我的done()方法中调用get()会返回异常,那么处理异常的适当方法是什么?

我特别关注可能的InterruptedExceptions。 JavaDoc示例简单地忽略了异常,但多年来我已经了解到吞咽异常导致难以调试的代码。

示例用法如下:

new SwingWorker<String, Void>() {

    @Override
    protected String doInBackground() throws Exception {
        // do long-running calculation
        return result;
    }

    @Override
    protected void done() {
        try {
            setTextField(get());
        } catch (InterruptedException e) {
            e.printStackTrace();  
        } catch (ExecutionException e) {
            e.printStackTrace();  
        }
    }
}.execute();

7 个答案:

答案 0 :(得分:11)

这是一篇旧帖子,但我想做一些澄清:

SwingWorker.get抛出InterruptedException,ExecutionException作为已检查的异常。

另外,它会抛出一个非常特定的未经检查的异常,即CancellationException。 当然它可能会抛出任何未经检查的异常,但是CancellationException不是“特殊”和意外的异常。在尝试调用get方法之后调用它会被抛出。

在doInBackground中抛出异常时抛出ExecutedException。 原始异常包含在ExecutionException中。 当调用get()方法时,将抛出ExecutionException。 取出原始异常并管理它的想法很好。 (正如Emil H指出的那样)。

取消选中CancellationException,我认为应该检查。 API实现的唯一借口是不要检查它是否有状态方法isCancelled()。
你可以:
- 测试isCancelled(),如果为true则不调用get(),因为它会抛出CancellationException
- 使用try-catch环绕get()并添加CancellationException,因为编译器不会请求unchecked - 未选中CancellationException这一事实让您可以自由地忘记所有这些并获得惊喜。
- 做任何事情,因为你不会取消工人

InterruptedException的。如果使用cancel(true)取消SwingThread,则doInBackground中的第一个可中断方法调用(确保Thread.sleep,this.wait,可能是某些IO方法)将抛出InterruptException。但是这个异常没有包含在ExecuteException中。 doInBackground将以中断的异常终止。如果它被捕获并转换为其他异常,那么这些将被忽略,因为此时取消已经在EDT上调用了SwingThread.done,如果完成调用了get,它只得到标准的CancellationException。 不是InterruptedException!

如果使用cancel(false)取消,则doInBackground内不会引发InterruptException。 如果使用cancel(true)取消,但doInBackground中没有可中断的方法调用,则相同。 在这些情况下,doInBackground将遵循其自然循环。此循环应测试isCancelled方法并正常退出。 如果doInBackground不这样做,它将永远运行。 我没有测试过时的存在,但我不相信。

对我来说,它只是一个灰色区域。 在哪些情况下,由get抛出InterruptedException?我想看一些简短的代码,因为我无法产生类似的异常。 : - )

P.S。 我在另一个问题和答案中记录了在doInBackground退出之前调用了取消时的完成和状态更改侦听器。 由于这是真的,这不是一个错误 - 在设计doInBackground方法时需要特别注意。如果您对此感兴趣,请参阅SwingWorker: when exactly is called done method?

答案 1 :(得分:2)

这在很大程度上取决于后台作业可能导致的错误类型。如果doInBackground中的作业抛出异常,它将作为嵌套的ExecutionException传播到done方法。在这种情况下,最佳做法是处理嵌套异常,而不是ExecutionException本身。

例如:如果工作线程抛出异常,指示数据库连接已丢失,则可能需要重新连接并重新启动作业。如果要完成的工作取决于已经使用的某种资源,那么提供使用重试或取消选择将是一个好主意。如果抛出的异常对用户没有任何影响,只需记录错误并继续。

从我记忆中来看,我相信InterruptedException在这里不会成为问题,因为你在done方法中调用了get方法,因为只有在等待get调用被中断时才会抛出InterruptedException。后台工作完成。如果发生类似意外事件,您可能希望显示错误消息并退出应用程序。

答案 2 :(得分:2)

这是一个接口问题,因为它是一个错误处理问题。许多应用程序添加了一些列出正在运行的后台作业的小表。其中一个例外中的异常可能会闪现产生错误的表行,或者执行一些具有破坏性的事件作为警报。这取决于异常的严重程度。我认为你可能要回答的更难的问题是我们谈论了多少可能不同类型的例外,以及它们的相对严重程度。

我认为一个简单的妥协可能是为您最严重的错误提供模式警报,而其他任何事情,只需记录事件,直到a)阻止用户继续进行,或b)用户关闭文档/窗口,此时,您可以在询问是否要保存任何未保存的缓冲区的同时显示后台处理任务期间发生的异常列表。

答案 3 :(得分:2)

我建议的是让错误一直传播到动作开始的地方。

例如,如果用户单击按钮以从数据源获取数据。如果出现问题,无论是凭据错误,网络错误,数据库错误还是其他任何问题,您都不适合在该工作线程中使用逻辑来尝试在那里解决它。

但是如果你让它传播到任务的开始位置,你可以从那里进行相应的错误更正,例如再次弹出凭据对话框,显示“再试一次”对话框,甚至显示错误消息。 / p>

答案 4 :(得分:0)

假设这是一个GUI应用程序,您可能希望提供有关异常时失败操作的可视反馈。

答案 5 :(得分:0)

我应该澄清我原来的帖子。不要简单地在done()方法中捕获某些Exception类型。在doInBackground()中执行的任何代码都可以抛出任何类型的Exception,只捕获问题中的异常会导致在EDT(事件调度线程或主GUI线程)上抛出异常。在使用SwingWorkers时,捕获done()方法中的所有异常类型只是一个好习惯。

@Override
protected void done()
{
    try
    {
        if(!super.isCancelled())
        {
            super.get();
        }
    }
    catch(Exception ex)
    {
        ex.printStackTrace();
    }
}

答案 6 :(得分:-3)

我猜你用C#没有得到很多这样的问题。您需要了解异常是什么并适当地处理它(通常是让它在堆栈中更进一步)。

InterruptedException - 在等待(粗略)时线程被中断(Thread.interrupt)时抛出。为什么要中断线程?通常你希望线程停止它正在做的事情 - 重置中断状态并退出。例如,如果在applet消失后它们会持续更长时间,那么PlugIn会中断applet线程。但是,在这种情况下,如果正确调用done,则不应该等待。因此,将异常包装在IllegalStateException中是合适的(并且API文档可能应该说明这一点)。这是一个非常糟糕的API。 publish / process模式可能更有意义。

ExecutionException - 你需要处理被包装的异常。如果您不期望特定类型的异常,请将其包装在未经检查的异常中。

一般来说,我建议将EDT上发生的事情与EDT发生的情况明确分开。因此,请避免在生产代码中使用SwingWorker