2012-12-18 28 views
9

Tôi đã đấu tranh với vấn đề về khả năng sử dụng của SwingWorker khi ăn bất kỳ ngoại lệ nào được ném trong tác vụ nền, ví dụ, được mô tả on this SO thread. Chủ đề đó đưa ra một mô tả tốt đẹp về vấn đề, nhưng không thảo luận về việc khôi phục ngoại lệ ban đầu.Trường hợp ngoại lệ SwingWorker bị mất ngay cả khi sử dụng các lớp bao bọc

Ứng dụng tôi đã được trao nhu cầu truyền bá ngoại lệ trở lên. Nhưng tôi đã không thể bắt được nó. Tôi đang sử dụng lớp wrapper SimpleSwingWorker từ this blog entry đặc biệt để thử và giải quyết vấn đề này. Đó là một lớp học khá nhỏ nhưng tôi sẽ repost nó ở cuối đây chỉ để tham khảo.

Mã gọi trông rộng rãi như

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 :-(
} 

Các wrappers:

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

Các tính năng của SimpleSwingWorkerdone()/get() phương pháp SwingWorker thực tế của sẽ được tự động gọi. Điều này, về mặt lý thuyết, sẽ trả lại bất kỳ ngoại lệ nào đã xảy ra trong nền. Trong thực tế, không có gì là bao giờ bị bắt, và tôi thậm chí không biết tại sao.

Lớp SimpleSwingWorker, để tham khảo, và không có gì elided cho ngắn gọn:

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

Tất cả điều này được dựa trên giả định sai. Đọc [javadoc cho phương thức get()] (http://docs.oracle.com/javase/6/docs/api/javax/swing/SwingWorker.html#get%28%29): nó ném một ExecutionException nếu tính toán nền đã ném một ngoại lệ. –

+0

Xem thêm [Hỏi đáp] này (http://stackoverflow.com/q/7053865/230513). – trashgod

+0

@JBNizet Có, đó là lý do tại sao các cuộc gọi() của SimpleSwingWorker nhận được(), bắt ExecutionException và trả về nó như là một RuntimeException mới. Đó không phải là vấn đề? Nếu không, thì chúng ta đang nói chuyện với nhau và bạn sẽ phải rõ ràng hơn. –

Trả lời

20

Quên wrapper của bạn, mà ăn những ngoại lệ, trong khi SwingWorker không. Dưới đây là cách sử dụng SwingWorker và cách bạn xử lý một ngoại lệ cụ thể được ném từ tác vụ nền:

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

Tôi đoán đây là lý do tại sao tôi cảm thấy thất vọng. Các cuộc gọi 'SimpleSwingWorker # done()' nhận được, thực hiện thử và bắt giữ các ngoại lệ. Nhưng bạn nói nó ăn chúng, và tôi không hiểu tại sao. Tôi đang đánh dấu câu trả lời này là câu trả lời chính xác và sau đó chỉ xóa mã; có lẽ rất nhiều cách để làm cho nó đúng nhưng không ai trong số đồng nghiệp của tôi có thể cho tôi biết lý do tại sao mã này không hoạt động. –

+1

+1 cho Generics 'SwingWorker ', 'SwingWorker được thiết kế tốt.' -100 :-), tôi nghĩ rằng không, tôi không đồng ý, tôi nhớ có phương pháp thực hiện trước khi SwingWorker đã được thêm vào chính thức Suns Api, – mKorbel

+0

@TiStrga: trình bao bọc ném chúng, nhưng không ai ngoại trừ trình xử lý ngoại lệ mặc định của EDT có thể bắt chúng. Vì vậy, về cơ bản: bạn không thể xử lý các trường hợp ngoại lệ được ném. –

3

Các wrapper dường như công trình như mong đợi. Tuy nhiên, việc triển khai của nó sẽ không bao giờ gọi done() nếu ngoại lệ xảy ra. Điều này không phù hợp với nhiều trường hợp. Nó có thể đơn giản hơn để gọi get() trong done(). Điều này sẽ ném bất kỳ ngoại lệ nào đã xảy ra trong doInBackground().

Không chắc chắn về cách ví dụ của bạn được cấu trúc, nhưng nó không hoạt động trong ứng dụng mà không có EDT. Vì vậy, gói thi công công nhân trong SwingUtilities.invokeLater đã giúp đỡ, ví dụ:

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

Ví dụ sau thực hiện in các ngoại lệ stack trace:

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

Cũng xem xét ví dụ này đơn giản mà thể hiện như thế nào để có được những trường hợp ngoại lệ xảy ra trong doInBackground() mà không sử dụng trình bao bọc. Trình bao bọc chỉ là người trợ giúp trong trường hợp bạn quên gọi 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

+1 cho 'invokeLater()'. Ném một ngoại lệ rõ ràng, như bạn đã chỉ ra, làm cho hiệu ứng dễ nhìn hơn. – trashgod

1

Tôi nghĩ mọi người đang làm quá phức tạp. Chỉ cần thử điều này:

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; 

Ngoại lệ bạn ném nhưng bị ăn sẽ được tiết lộ. Hai điểm sẽ giải thích lý do tại sao điều này hoạt động. Một, bạn chỉ bắt được ngoại lệ từ xa từ chuỗi gọi bạn là .get() kết quả. Cụ thể hơn: Để làm ví dụ trên không đồng bộ, chỉ cần di chuyển .execute() cao hơn trong mã. Khoảnh khắc bạn sẽ khám phá ra ngoại lệ từ xa là sau khi chuỗi asynch đã bị đánh bom và bạn .get() kết quả.Hai, bằng cách bắt tất cả các ngoại lệ, bạn sẽ bắt được tất cả ngoại lệ đặc biệt và phân lớp mà bạn có thể đã xây dựng mà chương trình gọi điện có thể chưa biết.

Các vấn đề liên quan