2010-09-03 8 views
9

Ho letto che tutto il codice che costruisce componenti Swing e gestisce gli eventi deve essere eseguito dal thread di invio eventi. Capisco come questo viene realizzato utilizzando il metodo SwingUtilities.invokeLater(). Si consideri il seguente codice in cui l'inizializzazione GUI viene fatta nel metodo mainDove viene chiamato il thread di invio dell'evento?

public class GridBagLayoutTester extends JPanel implements ActionListener { 
    public GridBagLayoutTester() { 
     setLayout(new GridBagLayout()); 
     GridBagConstraints gbc = new GridBagConstraints(); 

     JButton button = new JButton("Testing"); 
     gbc.fill = GridBagConstraints.HORIZONTAL; 
     gbc.anchor = GridBagConstraints.WEST; 
     gbc.gridx = 0; 
     gbc.gridy = 0; 
     gbc.gridwidth = 1; 
     button.addActionListener(this); 
     add(button, gbc); 
    } 

    public void actionPerformed(ActionEvent e) { 
     System.out.println("event handler code"); 
    } 

    public static void main(String[] args) { 
     JFrame frame = new JFrame("GridBagLayoutDemo"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
     Container contentPane = frame.getContentPane(); 
     contentPane.setLayout(new BorderLayout()); 
     contentPane.add(new GridBagLayoutTester(), BorderLayout.CENTER); 
     frame.setSize(800, 600); 
     frame.pack(); 
     frame.setVisible(true); 
     System.out.println("Exiting"); 
    } 
} 

Come è possibile che questo codice funziona perfettamente? Stiamo costruendo JFrame e chiamando un host di altri metodi nel thread principale. Non capisco esattamente dove l'EDT stia entrando in figura (quale codice sta eseguendo?). Anche il costruttore della classe GridBagLayoutTester viene chiamato dal metodo main, il che significa che EDT non lo sta eseguendo.

Insomma

  1. quando è l'EDT viene avviato? (la JVM avvia l'EDT insieme al metodo principale se l'EDT viene avviato durante l'esecuzione di questo codice?)
  2. Il codice del gestore eventi per il pulsante viene eseguito sull'EDT?

risposta

12

Il codice funziona perfettamente perché si sta costruendo il frame nel thread principale, prima che EDT abbia l'opportunità di interagire con esso. Tecnicamente, non dovresti farlo mai, ma tecnicamente puoi farlo in questa circostanza specifica perché non puoi interagire con JFrame finché non diventa visibile.

Il punto principale da sapere è che i componenti Swing non sono thread-safe. Ciò significa che non possono essere modificati da più di un thread allo stesso tempo. Questo è risolto assicurando che tutte le modifiche provengano dall'EDT.

L'EDT è un thread dedicato all'interazione dell'utente. Tutti gli eventi generati dall'utente vengono sempre eseguiti sull'EDT. Tutti gli aggiornamenti dell'interfaccia utente vengono eseguiti su EDT. Ad esempio, quando si chiama Component.repaint(), è possibile chiamare questo da qualsiasi thread. Questo semplicemente imposta un flag per contrassegnare il componente come se avesse bisogno di una vernice e l'EDT lo fa al suo prossimo ciclo.

L'EDT viene avviato automaticamente ed è strettamente collegato all'implementazione del sistema. È gestito bene all'interno della JVM. In genere, è correlato a un singolo thread nel sistema di finestre che gestisce l'interazione dell'utente. Naturalmente, questo è abbastanza dipendente dall'implementazione. La cosa bella è che non devi preoccuparti di questo. Devi solo sapere - se interagisci con qualsiasi componente di Swing, fallo sull'EDT.

Allo stesso modo, c'è un'altra cosa che è importante. Se si sta eseguendo un'elaborazione o un blocco di lunga durata per una risorsa esterna e lo si farà in risposta a un evento generato dall'utente, è necessario pianificarlo per eseguirlo nella propria discussione fuori dall'EDT. Se non si esegue questa operazione, l'interfaccia utente verrà bloccata mentre attende l'esecuzione dell'elaborazione di lunga durata. Esempi eccellenti sono il caricamento da file, la lettura da un database o l'interazione con la rete. È possibile verificare se si è in EDT (utile per creare metodi neutri che possono essere chiamati da qualsiasi thread) con il metodo SwingUtilities.isEventDispatchThread().

Qui ci sono due frammenti di codice che uso abbastanza frequentemente quando si scrive la programmazione swing che fare con l'EDT:

 
void executeOffEDT() { 
    if (SwingUtilities.isEventDispatchThread()) { 
    Runnable r = new Runnable() { 
     @Override 
     public void run() { 
     OutsideClass.this.executeOffEDTInternal(); 
     } 
    }; 
    new Thread(r).start(); 
    } else { 
    this.executeOffEDTInternal(); 
    } 
} 

void executeOnEDT() { 
    if (SwingUtilities.isEventDispatchThread()) { 
    this.executeOnEDTInternal(); 
    } else { 
    Runnable r = new Runnable() { 
     @Override 
     public void run() { 
     OutsideClass.this.executeOnEDTInternal(); 
     } 
    }; 
    SwingUtilities.invokeLater(r); 
    } 
} 
+0

Per quanto riguarda Component.repaint() Dubito che questo solo "imposta un flag", che in realtà mette in coda un evento di pittura (che saranno poi elaborati dal EDT). – jfpoilpret

+1

Il modello è lo stesso. Non è necessario conoscere l'interno del modo in cui viene gestito per utilizzare l'EDT con successo. –

+0

quindi la chiamata frame.setVisible() viene eseguita sull'EDT? – Stormshadow

1

1) non so se in new JFrame o in setVisible ma inizializzo su richiesta ed è quello che la fine del metodo main (oltre il filo processo principale) non termina il processo. l'EDT è stato lanciato ed è bloccato in loop in attesa del prossimo evento.

2) Definitivamente. Quel ciclo riceve dal sistema operativo l'evento, trova JButton e dice che l'evento è stato attivato. Il pulsante chiama quindi gli ascoltatori. Tutto ciò che accade nell'EDT.

È possibile rivedere il codice Swing che si chiama quando si desidera interrompere il processo (o chiudere la finestra principale) per cercare dove è terminato l'EDT ... che può darvi un indizio (lo farò in seguito ! :)

3

Il thread di invio eventi, come suggerisce il nome, viene chiamato da Swing ogni volta che è necessario elaborare un evento.

Nell'esempio fornito, il pulsante "Test" chiamerà automaticamente il metodo actionPerformed quando un evento azione deve essere elaborato. Pertanto, il contenuto del tuo metodo actionPerformed verrà richiamato dal thread di invio eventi.

per rispondere alle vostre due domande finali:

  • L'EDT viene avviato automaticamente al caricamento framework Swing. Non devi preoccuparti di iniziare questo thread, il JRE gestisce questo compito per te.
  • Il codice del gestore eventi è gestito dall'EDT. Tutti gli eventi generati dall'interfaccia Swing sono raggruppati e l'EDT è responsabile dell'esecuzione.
Problemi correlati