2011-06-29 15 views
22

Używam wątkowania w aplikacji za pośrednictwem klasy Swing Worker. Działa dobrze, ale mam złe przeczucie, że pokazuję okno dialogowe z komunikatem o błędzie w bloku try-catch. Czy może potencjalnie zablokować aplikację? Oto, na czym to teraz wygląda:Wyczerpujące obsługa wyjątków w Swing Worker

SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { 

    // Executed in background thread 
    public Void doInBackground() { 
     try { 
      DoFancyStuff(); 
     } catch (Exception e) { 

      e.printStackTrace(); 

      String msg = String.format("Unexpected problem: %s", e 
        .toString()); 

      //TODO: executed in background thread and should be executed in EDT? 
      JOptionPane.showMessageDialog(Utils.getActiveFrame(), 
        msg, "Error", JOptionPane.ERROR_MESSAGE, 
        errorIcon); 

     }//END: try-catch 

     return null; 
    } 

    // Executed in event dispatch thread 
    public void done() { 
     System.out.println("Done"); 
    } 
}; 

Czy można to zrobić w bezpieczny sposób za pomocą struktury Swing Worker? Czy nadrzędna metoda publish() jest tutaj dobrym tropem?

EDIT:

Czy to tak:

} catch (final Exception e) { 

    SwingUtilities.invokeLater(new Runnable() { 

     public void run() { 

      e.printStackTrace(); 

      String msg = String.format(
        "Unexpected problem: %s", e.toString()); 

      JOptionPane.showMessageDialog(Utils 
        .getActiveFrame(), msg, "Error", 
        JOptionPane.ERROR_MESSAGE, errorIcon); 

     } 
    }); 

} 

Wywołanie dostać w metodzie Sporządzono skutkowałoby w dwóch blokach try-catch, jako część obliczeniowa generuje wyjątki, więc myślę, że jest to czystsze w końcu.

Odpowiedz

13

Jedną z opcji jest użycie SwingUtilities.invokeLater(...) aby umieścić akcję na EDT

SwingUtilities.invokeLater(new Runnable(){ 
    @Override 
    public void run(){ 
     JOptionPane.showMessageDialog(
      Utils.getActiveFrame(), 
      msg, 
      "Error", 
      JOptionPane.ERROR_MESSAGE, 
      errorIcon); 
    } 
}); 

I jak zauważył SwingWorker jest zdolny do raportowania wyników pośrednich, ale trzeba zastąpić process(...), która jest wywoływana, gdy możesz wywołać publish(...).

Niezależnie od tego, dlaczego nie ustawić flagi, jeśli wystąpi wyjątek, a jeśli ta flaga jest ustawiona, pokaż okno dialogowe w done(), ponieważ jest bezpiecznie wykonane w EDT?

+4

Posługiwanie się nim w metodzie done() jest dokładnie tym, w jaki sposób podjąłem tę sytuację w przeszłości. invokeLater to również świetne rozwiązanie. – jzd

+0

tak i nie, ale odpowiedź na pytanie OP +1 – mKorbel

+1

1) Nigdy nie pozwalałem na uruchamianie żadnego kodu, który mógłby wyrzucić wyjątek w SwingWorker 2) PropertyChangeListener byłby złapać jakiekolwiek wyjątki od SwingWorker 3) wszystko wygląda jak budynek duży kłopoty idące w ten sposób :-) – mKorbel

1

Masz rację, naruszasz kardynalną zasadę Swing, która nie modyfikuje GUI w dowolnym miejscu poza wątkiem zdarzenia-wysyłki.

Jeśli to ja, to wyrzucę zdarzenie, które GUI nasłuchuje, aby wyświetlić komunikat o błędzie. Lub możesz po prostu owinąć wywołanie SwingWorker w próbie catch i pokazać tam dialog.

51

Właściwy sposób, aby zrobić to w następujący sposób:

SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() { 
    // Executed in background thread 
    protected Void doInBackground() throws Exception { 
     DoFancyStuff(); 
     return null; 
    } 

    // Executed in EDT 
    protected void done() { 
     try { 
      System.out.println("Done"); 
      get(); 
     } catch (ExecutionException e) { 
      e.getCause().printStackTrace(); 
      String msg = String.format("Unexpected problem: %s", 
          e.getCause().toString()); 
      JOptionPane.showMessageDialog(Utils.getActiveFrame(), 
       msg, "Error", JOptionPane.ERROR_MESSAGE, errorIcon); 
     } catch (InterruptedException e) { 
      // Process e here 
     } 
    } 
} 

NIE Należy starać się złapać wyjątki w wątku tła, ale raczej niech przechodzi do samego SwingWorker, a następnie można je dostać w metoda done(), wywołując get(), która zwykle zwraca wynik doInBackground() (Void w twojej sytuacji). Jeśli wyjątek został wyrzucony w wątku tła, to zostanie on zgłoszony jako get(), zawinięty wewnątrz ExecutionException.

Należy również pamiętać, że metody z ukrytymi SwingWorkerprotected i nie trzeba ich wykonywać public.

+0

to o próbie - catch - w końcu blok wewnątrz utworzonych klas zagnieżdżonych SwingWorkers Metody/Funkcjonalność :-) +1 – mKorbel

+0

to też działa, o ile testowałem, jednak w moim przypadku dodaje dużo kodu. Część obliczeniowa rzuca wyjątki, więc musiałbym dodać dwa bloki try-catch. Ale pamiętajcie o tym. – fbielejec

+2

W takim przypadku możemy się zastanawiać, dlaczego używasz 'SwingWorker' i nie używasz własnego wątku do pracy w tle, ponieważ nie używasz żadnej istotnej funkcji' SwingWorker'. Należy również pamiętać, że twój kod NIE ZALECA przerw w wątku tła. – jfpoilpret