2011-06-29 15 views
22

Estoy utilizando el enhebrado en la aplicación a través de la clase Swing Worker. Funciona bien, pero tengo un mal presentimiento al mostrar un diálogo de mensaje de error en el bloque try-catch. ¿Puede bloquear potencialmente la aplicación? Esto es lo que parece ahora:Manejo elegante de excepciones en 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"); 
    } 
}; 

¿Se puede hacer de una manera segura utilizando Swing Worker framework? ¿Está superando el método publish() una buena ventaja aquí?

EDIT:

hice así:

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

     } 
    }); 

} 

Calling llegar en el método de hacerlo resultaría en dos bloques try-catch, como la parte de cálculo arroja excepciones, así que creo que esto es limpiador al final.

Respuesta

13

Una opción es utilizar SwingUtilities.invokeLater(...) para publicar la acción en la EDT

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

Y como usted señaló, SwingWorker es capaz de informar de los resultados intermedios, pero necesitará para anular process(...), que se llama cuando Invoca al publish(...).

En cualquier caso, ¿por qué no establecer una bandera si se produce una excepción, y si ese indicador está configurado, mostrar el cuadro de diálogo en done() ya que se ejecuta de forma segura en el EDT?

+4

Manejarlo en el método done() es exactamente como he abordado esta situación en el pasado. invokeLater es una gran solución también. – jzd

+0

sí y no demasiado, pero responda a la pregunta de OP +1 – mKorbel

+1

1) Nunca he permitido que se ejecute ningún código que pueda arrojar Exception dentro de SwingWorker 2) PropertyChangeListener sería capturado como excepción de SwingWorker 3) todo parece como construir a lo grande problemas para ir de esta manera :-) – mKorbel

1

Tiene razón, está violando la regla cardinal de Swing, que no es modificar la GUI en ninguna parte excepto en el evento-envío-hilo.

Si fuera yo, lanzaría un evento que la GUI escucha para mostrar el mensaje de error. O bien, puede envolver la invocación del SwingWorker en un try catch y mostrar el diálogo allí.

51

La forma correcta de hacerlo es la siguiente:

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

NO debe tratar de capturar las excepciones en el subproceso de fondo, sino más bien dejarlos pasar a través de la propia SwingWorker, y entonces se puede obtener en el método done() llamando al get() que normalmente devuelve el resultado de doInBackground() (Void en su situación). Si se arrojó una excepción en el hilo de fondo, entonces get() lo arrojará, envuelto dentro de un ExecutionException.

Tenga en cuenta que los métodos overidden SwingWorker son protected y no es necesario que los haga public.

+0

que trata de try - catch - finally block dentro de clases anidadas creadas SwingWorkers methods/funcionality (ies) :-) +1 – mKorbel

+0

esto también funciona, por lo que he probado, sin embargo, en mi caso agrega mucho código. La parte computacional arroja Excepciones, así que tendría que agregar dos bloques de try-catch. Aunque hay que tenerlo en cuenta. – fbielejec

+2

Entonces, en este caso, podemos preguntarnos por qué está utilizando 'SwingWorker' y no usar su propio hilo para el trabajo en segundo plano, ya que no está utilizando ninguna característica' SwingWorker' relevante. Tenga en cuenta también que su código NO maneja las interrupciones del hilo de fondo. – jfpoilpret