2012-12-18 67 views
9

我一直在努力解決SwingWorker在後臺任務中拋出任何異常的可用性問題,例如描述爲on this SO thread。該線程提供了一個很好的問題描述,但不討論恢復原始異常。即使使用包裝類時SwingWorker異常也會丟失

我上傳的小程序需要向上傳播異常。但我一直無法抓住它。我正在使用this blog entry中的SimpleSwingWorker包裝類來嘗試解決此問題。這是一個相當小的班,但我會在最後重新發布它,僅供參考。

調用代碼看起來像廣泛

try { 
    // lots of code here to prepare data, finishing with 
    SpecialDataHelper helper = new SpecialDataHelper(...stuff...); 
    helper.execute(); // this will call get+done on the actual worker 
} catch (Throwable e) { 
    // used "Throwable" here in desperation to try and get 
    // anything at all to match, including unchecked exceptions 
    // 
    // no luck, this code is never ever used :-(
} 

的包裝:

class SpecialDataHelper extends SimpleSwingWorker { 
    public SpecialDataHelper (SpecialData sd) { 
     this.stuff = etc etc etc; 
    } 
    public Void doInBackground() throws Exception { 
     OurCodeThatThrowsACheckedException(this.stuff); 
     return null; 
    } 
    protected void done() { 
     // called only when successful 
     // never reached if there's an error 
    } 
} 

SimpleSwingWorker的特點是實際的SwingWorker的done()/get()方法被自動調用。理論上,這反映了背景中發生的任何異常。在實踐中,什麼都不會被捕獲,我甚至不知道爲什麼。

的SimpleSwingWorker類作參考,並一無所有的消隱爲簡潔:

import java.util.concurrent.ExecutionException; 
import javax.swing.SwingWorker; 

/** 
* A drop-in replacement for SwingWorker<Void,Void> but will not silently 
* swallow exceptions during background execution. 
* 
* Taken from http://jonathangiles.net/blog/?p=341 with thanks. 
*/ 
public abstract class SimpleSwingWorker { 
    private final SwingWorker<Void,Void> worker = 
     new SwingWorker<Void,Void>() { 
      @Override 
      protected Void doInBackground() throws Exception { 
       SimpleSwingWorker.this.doInBackground(); 
       return null; 
      } 

      @Override 
      protected void done() { 
       // Exceptions are lost unless get() is called on the 
       // originating thread. We do so here. 
       try { 
        get(); 
       } catch (final InterruptedException ex) { 
        throw new RuntimeException(ex); 
       } catch (final ExecutionException ex) { 
        throw new RuntimeException(ex.getCause()); 
       } 
       SimpleSwingWorker.this.done(); 
      } 
    }; 

    public SimpleSwingWorker() {} 

    protected abstract Void doInBackground() throws Exception; 
    protected abstract void done(); 

    public void execute() { 
     worker.execute(); 
    } 
} 
+3

這一切都是基於一個錯誤的假設。閱讀[javadoc的get()方法](http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html#get%28%29):它會拋出一個ExecutionException if後臺計算引發異常。 –

+0

另請參閱此[問與答](http://stackoverflow.com/q/7053865/230513)。 – trashgod

+0

@JBNizet是的,這就是爲什麼SimpleSwingWorker的done()調用get()會捕獲ExecutionException並將其重新拋出爲新的RuntimeException。這不是重點嗎?如果沒有,那麼我們正在談論對方,你必須更加明確。 –

回答

20

忘掉你的包裝,它吃掉了異常,而SwingWorker沒有。這裏是如何的SwingWorker應該使用,你應該如何處理從後臺任務拋出一個特定的異常:

class MeaningOfLifeFinder extends SwingWorker<String, Object> { 
    @Override 
    public String doInBackground() throws SomeException { 
     return findTheMeaningOfLife(); 
    } 

    @Override 
    protected void done() { // called in the EDT. You can update the GUI here, show error dialogs, etc. 
     try { 
      String meaningOfLife = get(); // this line can throw InterruptedException or ExecutionException 
      label.setText(meaningOfLife); 
     } 
     catch (ExecutionException e) { 
      Throwable cause = e.getCause(); // if SomeException was thrown by the background task, it's wrapped into the ExecutionException 
      if (cause instanceof SomeException) { 
       // TODO handle SomeException as you want to 
      } 
      else { // the wrapped throwable is a runtime exception or an error 
       // TODO handle any other exception as you want to 
      } 
     } 
     catch (InterruptedException ie) { 
      // TODO handle the case where the background task was interrupted as you want to 
     } 
    } 
} 
+0

我想這就是爲什麼我感到沮喪。 'SimpleSwingWorker#done()'調用get,try-catch並重新拋出異常。但你說它正在吃掉它們,我不明白如何或爲什麼。我將這個答案標記爲正確的,然後刪除代碼;可能有很多方法可以讓它正確無誤,但是我的同事們都不能告訴我爲什麼這些代碼不起作用。 –

+1

+1爲泛型'SwingWorker ','SwingWorker設計得很好。'-100 :-),我認爲不是,我不同意,我很想念在SwingWorker被添加到官方之前實現的方法太陽Api, – mKorbel

+0

@TiStrga:包裝器會拋出它們,但除EDT的默認異常處理程序之外,沒有人可以捕獲它們。所以基本上:你不能處理拋出的異常。 –

3

如預期的包裝似乎作品。但是,如果發生異常,它的實現將永遠不會調用done()。這不適用於許多情況。在done()中撥打get()可能會更簡單。這會拋出在doInBackground()中發生的任何異常。

不確定您的示例如何構建,但在沒有EDT的應用程序中無效。因此,包裝工人執行在SwingUtilities.invokeLater沒有幫助,即:

SwingUtilities.invokeLater(new Runnable() { 
    public void run() { 
     new SpecialDataHelper().execute(); 
    } 
}); 

下面的示例是打印的異常堆棧跟蹤:

public class Tester { 

    static class SpecialDataHelper extends SimpleSwingWorker { 
     public SpecialDataHelper() { 
     } 
     public Void doInBackground() throws Exception { 
      throw new Exception("test"); 
     } 
     protected void done() { 
     } 
    } 

    public static void main(String[] args) { 
     try{ 
      SwingUtilities.invokeLater(new Runnable() { 
       public void run() { 
        new SpecialDataHelper().execute(); 
       } 
      }); 
     } catch(Exception ex){ 
      ex.printStackTrace(); 
     } 
    } 
} 

還要考慮這個簡單的例子,說明如何讓發生在異常doInBackground()而不使用包裝。包裝只是一個幫手,以防您忘記撥打get()

import javax.swing.JOptionPane; 
import javax.swing.SwingUtilities; 
import javax.swing.SwingWorker; 

public class Tester { 
    static class Worker extends SwingWorker<Void,Void> { 
     @Override 
     protected Void doInBackground() throws Exception { 
      throw new Exception("test"); 
     } 
     @Override 
     protected void done() { 
      try { 
       get(); 
       JOptionPane.showMessageDialog(null, "Operation completed"); 
      } catch (Exception ex) { 
       JOptionPane.showMessageDialog(null, "Operation failed"); 
      } 
     } 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
       new Worker().execute(); 
      } 
     });   
    } 
} 
+2

用於'invokeLater()'的+1。如您所示,拋出一個明確的例外,使得效果更容易看清。 – trashgod

1

我認爲每個人都使這個太複雜了。試試這個:

String myResult="notSet"; 
try { 
    // from your example above 
    helper.execute(); // this will call get+done on the actual worker 
    myResult=helper.get(); 
} catch (Exception e) { 
// this section will be invoked if your swingworker dies, and print out why... 
    System.out.println("exception "); 
    e.printStackTrace() ; 
    myResult="Exception "+e.getMessage(); 
} 
return myResult; 

你會拋出異常但會被吃掉。有兩點可以解釋爲什麼這會起作用。其一,你只需從調用線程中捕獲遠程異常,就可以得到.get()結果。更詳細地說:要使上面的示例異步,只需將代碼中較高的.execute()移到上面。你會發現遠程異常的時刻是在異步線程已經轟炸並且你.get()結果之後。二,通過捕獲所有異常,您將捕獲您可能已經構建的調用程序可能不知道的所有特殊和子類異常。

相關問題