问题
I use SwingWorker in Java 6 to avoid running long-running code on the event dispatch thread.
If the call to get() in my done() method returns an exception, what is an appropriate way of handling the exception?
I'm particularly concerned about possible InterruptedExceptions. The JavaDoc example simply ignores the exception but I've learnt over the years that swallowing exceptions leads to hard-to-debug code.
A sample usage is as follows:
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();
回答1:
It's an old post, but I want to do some clarification:
SwingWorker.get throws InterruptedException, ExecutionException as checked exceptions.
Plus it throws a very specific unchecked exception that is CancellationException. Of course it could potentially throw any unchecked exception, but CancellationException is not an "exceptional" and unexpected one. It is thrown when you try to call get method after that has been called cancel.
ExecutedException is thrown when an Exception is thrown inside doInBackground. The original exception is wrapped inside an ExecutionException. When the get() method will be called, the ExecutionException will be thrown. The idea of take out the original exception and manage that is good. (As Emil H pointed out).
CancellationException is unchecked and in my opinion should be checked.
The only excuse for the API implementation not to have it as checked is that it has a status method isCancelled().
You can either:
- test isCancelled() and if is true do NOT call get() since it will throw CancellationException
- surround get() with try-catch and add the CancellationException, that since unchecked will not requested by compiler
- the fact that CancellationException is not checked leave you free to forget all that stuff and get a nice surprise.
- do anything because you won't cancel the worker
InterruptedException. If you cancel a SwingThread with cancel(true) the first interruptable method call (for sure Thread.sleep, this.wait, maybe some IO methods) in doInBackground will throw InterruptException. But this exception is not wrapped in an ExecuteException. The doInBackground is left to terminate with interrupted exception. If it is catched and converted to some other exception, those will be ignored because by this moment cancel has already invoked SwingThread.done on the EDT, and if done has called get, it has get just the standard CancellationException. Not an InterruptedException!
If you cancel with cancel(false) no InterruptException is raised inside doInBackground. The same if you cancel with cancel(true) but there are no interruptable method calls inside doInBackground. In these cases, the doInBackground will follow its natural loop. This loop should test the isCancelled method and exit gracefully. If the doInBackground doesn't do so, it will run forever. I've not tested for the presence of timeouts, but I not belive so.
For me, it remains just a gray area. In which cases is InterruptedException thrown by get? I'd like to see some short code, since I couldn't produce a similar exception. :-)
P.S. I've documented in another Question&Answer that the done and state change listener in case of cancellation are called before the doInBackground exits. Since this is true, this -that is not a bug- requires a special attention when designing doInBackground method. If you are intrested in this, see SwingWorker: when exactly is called done method?
回答2:
It depends very much on the type of errors that might result from the background job. If the job in doInBackground throws an exception, it will be propagated to the done method as a nested ExecutionException. The best practice in this case would be to handle the nested exception, rather than the ExecutionException itself.
For example: If the worker thread throws an exception indicating that the database connection has been lost, you'd probably want to reconnect and restart the job. If the work to be done depends on some kind of resource that turns out to already be in use, it would be a good idea to offer the use a retry or cancel choice. If the exception thrown doesn't have any implications for the user, just log the error and continue.
From what I can remember, I believe that the InterruptedException won't be an issue here since you make the get method call in the done method, since the InterruptedException will only be thrown if the get call is interrupted while waiting for the background job to finish. If an unexpected event like that were to occur you'd probably want to display an error message and exit the application.
回答3:
This is as much an interface question as it is an error handling question. A lot of apps add some small table that lists the running background jobs. An exception in one of them might flash the table row that produced an error, or do something as disruptive as present an alert. It depends on the severity of the exception. I think the harder question you'll probably have to answer is how many potentially different types of exceptions are we talking about, and what is their relative severity.
I think a simple compromise might be to present a modal alert for your most severe errors, and anything else, simply record the occurrence until it a) blocks the user from proceeding furhter or b) the user closes the document/window, at which time you can show a list of exceptions that happened during background processing tasks at the same time that you ask if you want to save any unsaved buffers for example.
回答4:
What I would recommend is to let the error propagate all the way up to where the action was started.
For example, if a user clicks on a button to fetch data from a data-source. If a problem occurs, whether it being a credential error, a network error, a database error, or whatever else, it wouldn't be appropriate for you to have logic within that worker thread to try to solve it right there.
But if you let it propagate to where the task was started, from there you can take the appropriate error corrections, such as popping the credentials dialog once more, showing a "try again" dialog or even showing an error message.
回答5:
Assume this is a GUI application, you may want to provide a visual feedback about failed operation upon exception.
回答6:
I should've clarified my original post. Don't simply catch certain Exception types in the done() method. Any code that executes in the doInBackground() could throw any type of Exception and only catching the Exceptions in your question can lead to Exceptions being thrown on the EDT (Event Dispatch Thread or main GUI thread). Catching all Exceptions types in the done() method is just a good habit to have when using SwingWorkers.
@Override
protected void done()
{
try
{
if(!super.isCancelled())
{
super.get();
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
回答7:
I guess you don't get many of these question with C#. You need to understand what the exception is and deal with it appropriately (which is usually to let it go further up the stack).
InterruptedException
- Thrown when a thread is interrupted (by Thread.interrupt
) when waiting (roughly). Why would you want a thread to be interrupted? Usually you want the thread to stop what it's doing - reset the interrupt state and exit. For instance, the PlugIn will interrupt applet threads if they continue for much longer after an applet should be gone. However, in this case provided done
is called correctly you shouldn't be waiting at all. Therefore, it would be appropriate to wrap the exception in an IllegalStateException
(and the API docs should probably state that). It's a really bad API. The publish
/process
mode probably makes more sense.
ExecutionException
- You need to deal with the wrapped exception. If you are not expecting a particular type of exception, wrap it in an unchecked exception.
Generally I would suggest a clear separation between what happens on the EDT and what happens off the EDT. Therefore, avoid SwingWorker
in production code.
来源:https://stackoverflow.com/questions/791223/how-should-i-handle-exceptions-when-using-swingworker