El subproceso de distribución de eventos (EDT) es un subproceso en segundo plano que se utiliza en Java para procesar eventos de la cola de eventos de la interfaz gráfica de usuario Abstract Window Toolkit (AWT) . Es un ejemplo del concepto genérico de programación basada en eventos , que es popular en muchos otros contextos además de Java, por ejemplo, navegadores web o servidores web .
Los eventos son principalmente eventos de actualización que hacen que los componentes de la interfaz de usuario se redibujen a sí mismos, o eventos de entrada de dispositivos de entrada como el mouse o el teclado. El AWT utiliza un modelo de pintura de un solo subproceso en el que todas las actualizaciones de pantalla deben realizarse desde un solo subproceso. El subproceso de envío de eventos es el único subproceso válido para actualizar el estado visual de los componentes de la interfaz de usuario visibles. La actualización de los componentes visibles desde otros subprocesos es la fuente de muchos errores comunes en los programas Java que usan Swing . [1] El subproceso de envío de eventos se denomina trabajador primordial en Adobe Flash y subproceso de UI en SWT , .NET Framework y Android .
Una aplicación de software normalmente consta de varios subprocesos y una única estructura de datos GIT. Esto significa que GIT es una estructura de datos compartida y se necesita cierta sincronización para garantizar que solo un subproceso acceda a ella a la vez. Aunque AWT y Swing exponen los métodos ( inseguros para subprocesos ) para crear y acceder a los componentes de la GUI y estos métodos son visibles para todos los subprocesos de la aplicación, de la misma manera en otros marcos de GUI, solo un único subproceso de despacho de eventos tiene derecho a ejecutar estos métodos. [2] [3] [4] Dado que los programadores a menudo pasan por alto este requisito, los Look and Feels de terceros , como Substance, llegan al extremo de negarse a instanciar cualquier componente Swing cuando no se ejecuta dentro del subproceso de despacho de eventos, [5] para evitar tal error de codificación. El acceso a la GUI está serializado y otros subprocesos pueden enviar algún código para que se ejecute en el EDT a través de una cola de mensajes EDT .
Es decir, al igual que en otros marcos de GUI, el subproceso de envío de eventos pasa su vida enviando mensajes: mantiene una cola de mensajes de acciones que se deben realizar a través de la GUI. Estas solicitudes son enviadas a la cola por el sistema y cualquier subproceso de aplicación. EDT las consume una tras otra y responde actualizando los componentes de la GUI. Los mensajes pueden ser acciones conocidas o involucrar devoluciones de llamadas, las referencias a métodos de usuario que deben ejecutarse por medio de EDT.
El requisito importante que se impone a todos los mensajes es que deben ejecutarse rápidamente para que la GUI siga respondiendo. De lo contrario, el bucle de mensajes se bloquea y la GUI se congela.
Existen varias soluciones para enviar código al EDT y realizar tareas largas sin bloquear el bucle.
Los componentes de la GUI admiten listas de devoluciones de llamadas, llamadas Listeners, que normalmente se completan cuando se crean los componentes. EDT ejecuta los listeners cuando el usuario activa los componentes de alguna manera (se hace clic en un botón, se mueve el mouse, se selecciona un elemento, se pierde el foco, se cambia el tamaño del componente, etc.).
Se utiliza para tareas breves que deben acceder o modificar la GUI periódicamente o en un momento específico javax.swing.Timer
. Se puede considerar como un componente de GUI invisible, cuyos oyentes están registrados para activarse en momentos específicos.
Equivalentes
System.Windows.Forms.Timer
- Marco .NETflash.utils.Timer
- Adobe FlashOtros subprocesos de la aplicación pueden pasar algún código para que se ejecute en el subproceso de envío de eventos mediante SwingUtilities
clases auxiliares (o EventQueue
si está haciendo AWT ). El código enviado debe estar envuelto con un Runnable
objeto. Dos métodos de estas clases permiten:
SwingUtilities.invokeAndWait(Runnable)
o EventQueue.invokeAndWait(Runnable)
)SwingUtilities.invokeLater(Runnable)
o EventQueue.invokeLater(Runnable)
)del hilo de envío de eventos.
El método invokeAndWait()
nunca debe llamarse desde el hilo que distribuye el evento, ya que generará una excepción . El método SwingUtilities.isEventDispatchThread()
o EventQueue.isDispatchThread()
puede llamarse para determinar si el hilo actual es el hilo que distribuye el evento.
El código suministrado mediante invokeLater
y invokeAndWait
al EDT debe ser lo más rápido posible para evitar que se bloquee. Normalmente, su finalidad es entregar el resultado de un cálculo largo a la interfaz gráfica de usuario (GUI).
Tanto la ejecución de una tarea en otro hilo como la presentación de los resultados en la EDT se pueden combinar mediante el patrón de diseño worker . La javax.swing.SwingWorker
clase, desarrollada por Sun Microsystems , es una implementación del patrón de diseño worker y, a partir de Java 6, forma parte de la distribución Swing estándar. SwingWorker se invoca normalmente desde el evento ejecutado por la EDT Listener para realizar una tarea larga con el fin de no bloquear la EDT.
SwingWorker < Documento , Void > trabajador = nuevo SwingWorker < Documento , Void > () { público Documento doInBackground () lanza IOException { devolver loadXML (); // tarea pesada } público void hecho () { intentar { Documento doc = obtener (); mostrar ( doc ); } atrapar ( Excepción ex ) { ex . printStackTrace (); } } }; trabajador . ejecutar ();
Si usas Groovy y groovy.swing.SwingBuilder
, puedes usar doLater()
, doOutside()
, y edt()
. Luego puedes escribirlo de forma más sencilla, así:
doOutside { def doc = loadXML () // tarea pesada edt { display ( doc ) } }
System.ComponentModel.BackgroundWorker
- Marco .NETflash.system.Worker
- Adobe Flashandroid.os.AsyncTask
- AndroidNormalmente, EDT crea SwingWorker para tareas extensas mientras maneja eventos de devolución de llamada (Listener). Al generar un subproceso de trabajo, EDT procede a manejar el mensaje actual sin esperar a que el trabajador termine. A menudo, esto no es deseable.
A menudo, su EDT maneja una acción de componente GUI, que exige que el usuario haga una elección por medio de otro cuadro de diálogo, como JFileChooser, que aparece, permanece responsivo mientras el usuario elige su opción y la acción continúa con el archivo seleccionado solo después de presionar el botón "Aceptar". Verá, esto lleva tiempo (el usuario responde en cuestión de segundos) y necesita una GUI responsiva (los mensajes aún se envían a EDT) durante todo este tiempo mientras EDT está bloqueado (no maneja mensajes más nuevos, por ejemplo, JFileChooser, en la cola antes de que se cierre el cuadro de diálogo y finalice la acción del componente actual). El círculo vicioso se rompe cuando EDT ingresa en un nuevo bucle de mensajes, que envía los mensajes de manera normal hasta que llega el "diálogo modal terminado" y el procesamiento normal de mensajes se reanuda desde la posición bloqueada en la acción del componente.
El proyecto de código abierto Foxtrot emula el bombeo de bucle de mensajes Swing para proporcionar el mecanismo de ejecución "sincrónico" para tareas de usuario arbitrarias, que continúa solo después de que el trabajador completa la tarea.
botón .addActionListener ( new ActionListener () { público void actionPerformed ( ActionEvent e ) { botón .setText ( " Durmiendo..." ) ; Cadena texto = null ; try { texto = ( Cadena ) Trabajador.post ( nueva Tarea () { objeto público ejecutar () lanza Excepción { Hilo.sleep ( 10000 ) ; devolver " ¡Durmió!" ; } } ) ; } catch ( Excepción x ) ... botón .setText ( texto ) ;algoMás (); } });
Desde Java 1.7, Java proporciona una solución estándar para bucles de mensajes secundarios personalizados al exponer createSecondaryLoop () en el sistema EventQueue ().
javax.swing
( Documentación Javadoc de la API Swing )java.awt
( Documentación Javadoc de la API de AWT )