5

Ho trovato molti tutorial ed esempi sull'uso corretto dell'EDT, vorrei tuttavia sapere come si dovrebbe andare al contrario: controllare un applicazione complessa che ha una GUI Swing e molte funzionalità che coinvolgono lunghe operazioni di rete e trovano dove l'EDT è utilizzato in modo improprio.Come controllare un'applicazione Swing per l'uso corretto dell'EDT (thread DIspatch evento)

Ho scoperto che

SwingUtilities.isEventDispatchThread() 

potrebbe essere utilizzato per verificare se un pezzo di codice si trova all'interno del EDT o no, così ho potuto verificare che tutte le operazioni lunghe non capita di essere posti all'interno dove SwingUtilities .isEventDispatchThread() restituisce true.

È giusto? c'è qualcosa di meglio che potrei in un modo di debugare l'intera applicazione alla ricerca di un uso scorretto dell'EDT? Grazie.

+3

Date un'occhiata a: http://weblogs.java.net/blog/alexfromsun/archive/2006/02/debugging_swing.html – kiheru

+2

le strategie tipiche di controllo sono il contrario: lo trovano luoghi dove si accedere ai componenti di Swing fuori dall'EDT (vs controllare se il codice di lunga durata non si verifica sull'EDT) - logicamente, quest'ultimo non è possibile senza aggiungere codice nel luogo che si sospetta sia di lunga durata – kleopatra

+0

ANd come si fa trovare luoghi in cui si accede ai componenti di Swing fuori dall'EDT? – dendini

risposta

6

È giusto?

Sì, controllare il valore di SwingUtilities.isEventDispatchThread() è un modo per verificare se il codice si trova sul thread di Invio eventi (EDT) o meno.

Un altro modo sarebbe quello di visualizzare o stampare Thread.currentThread().getName(). L'EDT ha quasi sempre il nome "AWT-EventQueue-0".

Questo pezzo di codice elegante viene dall'articolo Debugging Swing, the final summary. Tuttavia, non è un debugger Swing completo. Questo codice controlla solo le violazioni del riverniciare.

L'articolo elenca altri metodi di debug più completi.

import javax.swing.JComponent; 
import javax.swing.RepaintManager; 
import javax.swing.SwingUtilities; 

public class CheckThreadViolationRepaintManager extends RepaintManager { 
    // it is recommended to pass the complete check 
    private boolean completeCheck = true; 

    public boolean isCompleteCheck() { 
     return completeCheck; 
    } 

    public void setCompleteCheck(boolean completeCheck) { 
     this.completeCheck = completeCheck; 
    } 

    public synchronized void addInvalidComponent(JComponent component) { 
     checkThreadViolations(component); 
     super.addInvalidComponent(component); 
    } 

    public void addDirtyRegion(JComponent component, int x, int y, int w, int h) { 
     checkThreadViolations(component); 
     super.addDirtyRegion(component, x, y, w, h); 
    } 

    private void checkThreadViolations(JComponent c) { 
     if (!SwingUtilities.isEventDispatchThread() 
       && (completeCheck || c.isShowing())) { 
      Exception exception = new Exception(); 
      boolean repaint = false; 
      boolean fromSwing = false; 
      StackTraceElement[] stackTrace = exception.getStackTrace(); 
      for (StackTraceElement st : stackTrace) { 
       if (repaint && st.getClassName().startsWith("javax.swing.")) { 
        fromSwing = true; 
       } 
       if ("repaint".equals(st.getMethodName())) { 
        repaint = true; 
       } 
      } 
      if (repaint && !fromSwing) { 
       // no problems here, since repaint() is thread safe 
       return; 
      } 
      exception.printStackTrace(); 
     } 
    } 
} 
+0

Sono più interessato alle tecniche per verificare che l'EDT sia usato correttamente rispetto ai modi di trovare se sono dentro o fuori l'EDT (anche perché isEventDispatchThread sembra fare il suo lavoro). Qualcuno ha suggerito di fare il contrario e di "trovare i punti in cui si accede ai componenti di Swing dall'EDT" – dendini

2

Un modo per controllare l'uso corretto di EDT di un'intera applicazione consiste nell'utilizzare un agente java. Il seguente codice è una versione migliorata dell'agente pubblicato sotto Debugging Swing, the final Summary. Funziona con ASM 4.1. Crea un Jar contenente asm-all-4.1.jar (scompattato), il codice compilato e un manifest specificando l'agente come Premain-Class e procedendo.

/** 
* A java agent which transforms the Swing Component classes in such a way that a stack 
* trace will be dumped or an exception will be thrown when they are accessed from a wrong thread. 
* 
* To use it, add 
* <pre> 
* -javaagent:${workspace_loc:MyProject/tool/util/swingEDTCheck}/swingEDTCheck.jar 
* </pre> 
* 
* to the VM arguments of a run configuration. This will cause the stack traces to be dumped. 
* 
* Use 
* <pre> 
* -javaagent:${workspace_loc:MyProject/tool/util/swingEDTCheck}/swingEDTCheck.jar=throw 
* </pre> 
* to throw exceptions. 
* 
*/ 
public class SwingEDTCheckAgent { 

    public static void premain(String args, Instrumentation inst) { 
     boolean throwing = false; 
     if ("throw".equals(args)) { 
      throwing = true; 
     } 
     inst.addTransformer(new Transformer(throwing)); 
    } 

    private static class Transformer implements ClassFileTransformer { 

     private final boolean throwing; 

     public Transformer(boolean throwing) { 
      this.throwing = throwing; 
     } 

     @Override 
     public byte[] transform(ClassLoader loader, 
      String className, 
      Class classBeingRedefined, 
      ProtectionDomain protectionDomain, 
      byte[] classfileBuffer) 
      throws IllegalClassFormatException { 
      // Process all classes in javax.swing package which names start with J 
      if (className.startsWith("javax/swing/J")) { 
       ClassReader cr = new ClassReader(classfileBuffer); 
       ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_FRAMES); 
       ClassVisitor cv = new EdtCheckerClassAdapter(cw, throwing); 
       cr.accept(cv, 0); 
       return cw.toByteArray(); 
      } 
      return classfileBuffer; 
     } 
    } 

    private static class EdtCheckerClassAdapter extends ClassVisitor { 

     private final boolean throwing; 

     public EdtCheckerClassAdapter(ClassVisitor classVisitor, boolean throwing) { 
      super(Opcodes.ASM4, classVisitor); 
      this.throwing = throwing; 
     } 

     @Override 
     public MethodVisitor visitMethod(final int access, 
      final String name, 
      final String desc, 
      final String signature, 
      final String[] exceptions) { 
      MethodVisitor mv = 
       cv.visitMethod(access, name, desc, signature, exceptions); 

      if (name.startsWith("set") || name.startsWith("get") || name.startsWith("is")) { 
       return new EdtCheckerMethodAdapter(mv, throwing); 
      } else { 
       return mv; 
      } 
     } 
    } 

    private static class EdtCheckerMethodAdapter extends MethodVisitor { 

     private final boolean throwing; 

     public EdtCheckerMethodAdapter(MethodVisitor methodVisitor, boolean throwing) { 
      super(Opcodes.ASM4, methodVisitor); 
      this.throwing = throwing; 
     } 

     @Override 
     public void visitCode() { 
      mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/awt/EventQueue", "isDispatchThread", "()Z"); 
      Label l1 = new Label(); 
      mv.visitJumpInsn(Opcodes.IFNE, l1); 
      Label l2 = new Label(); 
      mv.visitLabel(l2); 

      if (throwing) { 
       // more Aggressive: throw exception 
       mv.visitTypeInsn(Opcodes.NEW, "java/lang/RuntimeException"); 
       mv.visitInsn(Opcodes.DUP); 
       mv.visitLdcInsn("Swing Component called from outside the EDT"); 
       mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V"); 
       mv.visitInsn(Opcodes.ATHROW); 

      } else { 
       // this just dumps the Stack Trace 
       mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Thread", "dumpStack", "()V"); 
      } 
      mv.visitLabel(l1); 
     } 
    } 
} 
+0

Sono nuovo all'agente java. Puoi fornire istruzioni più dettagliate su come utilizzare la classe Swing EDTCheckAgent in un progetto esistente? – peterboston

+0

Ho aggiornato la risposta. Descrive già come impacchettarlo, ora ho aggiunto l'opzione -javaagent :. Potrebbe essere sensato creare un progetto github per questo. Cosa ne pensi? Interessato a farlo? – ruediste

+0

Grazie per il tuo aggiornamento. In realtà sono riuscito a farlo funzionare con la mia applicazione. Ma sembra che non riesca a trovare nulla di sbagliato dalla mia applicazione. Con RepaintManager vengono segnalate diverse violazioni e ho verificato che i risultati sono violazioni. – peterboston