2010-02-11 12 views
7

Estoy desarrollando una aplicación pequeña, que tendría la GUI Swing. La aplicación está realizando una tarea de E/S en otro subproceso, cuando termine el subproceso, la GUI se debe actualizar de forma correspondiente para reflejar el resultado de la operación del subproceso. La clase que se ejecuta en un (worker, non-GUI) tiene un objeto pasado en contructor que se usaría para actualizar la GUI, así que no necesito poner cosas de GUI en una clase que no sea GUI, sino pasar el objeto para actualizar la GUI a esa clase. Según tengo entendido, la forma de leer aquí, las opciones de seguridad (hilo/balanceo) para actualizar (cambiar) la GUI de Swing sería usar javax.swing.SwingUtilities.invokeLater(), javax.swing.SwingUtilities.invokeLaterWait() y/o javax.swing.SwingWorker() que básicamente están haciendo lo mismo. Este problema de enhebrado con Swing es un poco confuso para mí, y necesito usar hilos para hacer algo significativo en aplicaciones GUI y GUI no colgado mientras proceso en EDT, entonces lo que me interesa por ahora es esto:Actualizando el componente Swing desde otro hilo con invokeLater o SwingWorker

  1. son invokeLater y invokeLaterWait como el envío de mensajes a EDT y esperar a que lo haga cuando se termina de procesar los mensajes que han sido antes de esa llamada?

  2. ¿es cierto aspecto de la seguridad de hilo Swing, hay que hacer algo como esto:

    interface IUPDATEGUI { 
        public void update(); 
    } 
    
    // in EDT/where I can access components directly  
    class UpdateJList implements IUPDATEGUI {  
        public void update() {  
        // update JList...  
        someJList.revalidate();  
        someJList.repain();  
        }  
    }  
    
    class FileOperations implements Runnable { 
        private IUPDATEGUI upObj; 
        List<File> result = new ArrayList<File>; // upObject is accessing this 
    
        public void FileOperations(IUPDATEGUI upObj) { 
        this.upObj = upObj; 
        } 
    
        private void someIOTask() { 
        // ... 
        // IO processing finished, result is in "result" 
        } 
    
        public void run() { 
        someIOTask(); 
        javax.swing.SwingUtilities.invokeLater(new Runnable() { 
         public void run() { 
         upObj.update(); // access result and update JList 
         } 
        };); 
        } 
    } 
    

En caso de que esto no es correcto, entonces ¿cómo debe hacerse esto?

Si pudiera, yo preferiría usar invokeLater en lugar de SwingWorker si es posible, porque no tendría que cambiar toda mi clase y es de alguna manera más ordenada/distinta mí (como el envío de un mensaje en aplicaciones Win32).

Gracias de antemano.

Respuesta

0

invokeLater está bien. Esto pone la llamada en la cola de eventos de AWT, para que se ejecute en el EDT a su debido tiempo. Su programa continuará ejecutándose y no espera a que se llame su llamada.

+0

¿Las llamadas a revalidar() y volver a pintar() son necesarias después de actualizar el componente en este caso? – Mil

5

El uso de invokeLater() y invokeAndWait() transfiere el parámetro Runnable a la cola en espera de ejecución en el EDT. Entonces, llamar al invokeLater() hará que Runnable se ejecute en el EDT cuando el EDT pueda procesar la solicitud. invokeAndWait() simplemente espera (en el hilo de llamada) hasta que esta ejecución tenga lugar.

El uso de SwingWorker es ideal si desea realizar tareas en segundo plano que notifiquen al EDT al final de la ejecución o en estados intermedios. Un ejemplo sería pasar el progreso actual de un proceso a JProgressBar.

Para su ejemplo, parece que SwingWorker es una mejor opción, pero si no desea cambiar su código demasiado, entonces llamando al invokeLater() cuando el proceso esté hecho estará bien.

+1

¿Puede decirme algo más por qué cree que SwingerWorker sería una mejor opción en mi caso? – Mil

+0

Creo que lo tengo. Corrígeme si me equivoco, básicamente puedo actualizar los componentes de la GUI en ambos métodos de la clase SwingWorker, es decir, podría actualizar JProgressBar en doInBackground() y al final, cuando termine, podría actualizar JList en done() método de la clase SwingWorker? – Mil

+2

cuando desee publicar actualizaciones intermedias en la GUI mientras el proceso aún se está ejecutando, use el método publish() de swingworker que invoca el método process() en el EDT. Y se llama al método done() cuando el proceso finaliza también en el EDT. Entonces tiene dos opciones para actualizar la GUI. Uno mientras el proceso se está ejecutando (publicar) y cuando está listo (hecho). Lea esto: http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html es realmente útil. –

3

Recomendaría no usar el invokeAndWait hasta java 7. Encontré un falso despertador en este método que puede causar errores muy dolorosos. Para mí condujo a algunas excepciones de puntero nulo muy raras y difíciles de depurar.

http://bugs.sun.com/view_bug.do?bug_id=6852111

Se fija como de Java 7 b77.

+0

+1 para publicar sobre el despertar espurio ... el "esperar en un bucle" es un modismo estándar para las condiciones, así que me pregunto por qué no lo han hecho aquí para empezar. –

+0

Publiqué el código para "corregir" que hace años. En realidad, no debería haber un problema porque las JVM de Sun nunca se despiertan de forma espuria. Si ve NPE, puede ser por otras causas./En cualquier caso, 'invokeAndWait' tiende a causar bloqueos, por lo que debe evitarse. –

+0

Vea este par de CR antiguas: http://bugs.sun.com/view_bug.do?bug_id=4974934 http://bugs.sun.com/view_bug.do?bug_id=4932181 –

Cuestiones relacionadas