Uso del modo de roscado de swingworker
Es muy importante que los desarrolladores de swing usen la concurrencia con cuidado. Un buen programa de swing utiliza mecanismos de concurrencia para crear interfaces de usuario que no pierden respuesta, sin importar qué tipo de interacción del usuario, el programa siempre puede responder a ella. Para crear un programa receptivo, los desarrolladores deben aprender a usar múltiples lectura en el marco Swing.
Un desarrollador de swing se ocupará de los siguientes tipos de hilos:
(1) Presiones iniciales, dichos subprocesos ejecutarán el código de aplicación de inicialización.
(2) El hilo de despacho de eventos, todos los códigos de procesamiento de eventos se ejecutan aquí. La mayoría del código que interactúa con el marco Swing también debe ejecutar este hilo.
(3) Los hilos de trabajadores, también conocidos como hilos de fondo, realizarán tareas que requieren todas las tareas.
Los desarrolladores no necesitan crear estos hilos explícitamente en su código: son proporcionados por el marco de tiempo de ejecución o swing. El trabajo de los desarrolladores es utilizar estos hilos para crear programas de swing sensibles y sensibles.
Al igual que todos los demás programas que se ejecutan en las plataformas Java, un programa Swing puede crear hilos y grupos de hilos adicionales, lo que requiere el uso del enfoque que se introducirá en este artículo. Este artículo presentará los tres hilos anteriores. La discusión de los hilos de los trabajadores implicará usar la clase Javax.Swing.SwingWorker. Esta clase tiene muchas características útiles, incluida la comunicación y la colaboración entre tareas de hilo de trabajadores y otras tareas de hilo.
1. Hilo inicial
Cada programa genera una serie de hilos al comienzo de la lógica de la aplicación. En un programa estándar, solo hay uno de esos hilos: este hilo llamará al método principal en la clase principal del programa. En un applet, el hilo inicial es un constructor del objeto Applet, que llamará al método init; Estas acciones pueden ejecutarse en un solo hilo, o en dos o tres hilos diferentes, todos dependiendo de la implementación específica de la plataforma Java. En este artículo, llamamos a este tipo de hilos iniciales de hilo.
En un programa de swing, no hay mucho que hacer en el hilo inicial. Su tarea más básica es crear un objeto ejecutable que inicialice la GUI y orquesta los objetos utilizados para ejecutar eventos en el hilo de despacho de eventos. Una vez que se crea la GUI, el programa será impulsado principalmente por eventos de la GUI, cada uno de los cuales causará la ejecución de un evento en el hilo de despacho del evento. El código del programa puede orquestar tareas adicionales a los hilos basados en eventos (siempre que se ejecuten rápidamente para que no interfieran con el procesamiento de eventos) o crear hilos de trabajadores (se usa para realizar tareas que consumen mucho tiempo).
Una tarea de creación de GUI de orquestación de hilo inicial es llamar a javax.swing.swingutilities.invokelater o javax.swing.swingutilidades.invokeandwait. Ambos métodos toman un parámetro único: Runnable se usa para definir nuevas tareas. La única diferencia entre ellos es: Invokerlater solo orquesta la tarea y las devoluciones; InvokeandWait esperará a que se ejecute la tarea antes de regresar.
Vea el siguiente ejemplo:
SwingUtility.InvokElater (new Runnable ()) {public void run () {CreateAndShowGui (); }} En Applet, la tarea de crear la GUI debe colocarse en el método init y usar InvokeandWait; De lo contrario, el proceso inicial será posible antes de que se cree la GUI, lo que puede causar problemas. En otros casos, las tareas de creación de GUI de orquestación suelen ser las últimas en el hilo inicial que se ejecutará, por lo que tanto usan Invokelater o Invokeandwait.
¿Por qué el hilo inicial no crea la GUI directamente? Porque casi todo el código utilizado para crear e interactuar con los componentes Swing debe ejecutarse en el hilo de despacho de eventos. Esta restricción se discutirá a continuación.
2. Hilo de distribución de eventos
El código de procesamiento del evento Swing se ejecuta en un hilo especial, que se llama hilo de despacho del evento. La mayor parte del código que llama al método Swing se ejecuta en este hilo. Esto es necesario porque la mayoría de los objetos de swing son "no sean seguros".
Puede pensar en la ejecución del código como realizar una serie de tareas cortas en el hilo de despacho de eventos. La mayoría de las tareas se convocan mediante métodos de manejo de eventos, como ActionListener.ActionPerformed. Las tareas restantes serán orquestadas por el código del programa, utilizando Invokelater o InvokeandWait. Las tareas en el hilo de despacho de eventos deben poder ejecutarse rápidamente. De lo contrario, los eventos sin procesar se transmitirán y la interfaz de usuario se volverá "receptiva".
Si necesita determinar si su código se ejecuta en el hilo de envío de eventos, llame a javax.swing.swingutility.iseventDispatchThread.
3. Hilos de trabajadores y swingworker
Cuando un programa Swing necesita realizar una tarea larga, generalmente se realizará utilizando un hilo de trabajadores. Cada tarea se ejecuta en un hilo de trabajadores, que es una instancia de la clase javax.swing.swingworker. La clase Swingworker es una clase abstracta; Tienes que definir su subclase para crear un objeto Swingworker; Por lo general, use clases internas anónimas para hacer esto.
Swingworker proporciona algunas características de comunicación y control:
(1) La subclase del swingworker puede definir un método, hecho. Cuando se completa la tarea de fondo, el hilo de despacho de eventos la llamará automáticamente.
(2) La clase de swingworker implementa java.util.concurrent.future. Esta interfaz permite que las tareas de fondo proporcionen un valor de retorno a otros hilos. Los métodos en esta interfaz también proporcionan las funciones que permiten la ruina de las tareas de fondo y determinan si las tareas de fondo se han completado o revocado.
(3) Las tareas de fondo pueden proporcionar resultados intermedios llamando a swingworker.publish, y el hilo de despacho de eventos llamará a este método.
(4) Las tareas de fondo pueden definir las propiedades de unión. Los cambios en el atributo de enlace activarán eventos, y el hilo de despacho de eventos llamará al controlador de eventos para manejar estos eventos activados.
4. Tareas simples de backend
Aquí hay un ejemplo, esta tarea es muy simple, pero es una tarea potencialmente que requiere mucho tiempo. Tumbleitem Applet importa una serie de archivos de imagen. Si estos archivos de imagen se importan a través del hilo inicial, habrá un retraso antes de que aparezca la GUI. Si estos archivos de imagen se importan en el hilo de envío de eventos, la GUI puede no responder temporalmente.
Para resolver estos problemas, la clase TumbleItem crea y ejecuta una instancia de la clase StringWorker cuando se inicializa. El método DoInbackground de este objeto se ejecuta en un hilo de trabajadores, importa la imagen en una matriz Imageicon y devuelve una referencia a él. Luego, el método hecho se ejecuta en el hilo de despacho de eventos, obtenga la referencia devuelta y lo coló en el IMG de la variable miembro de la clase Applet. Hacer esto permite que la clase Tumbleitem cree una GUI inmediatamente sin tener que esperar a que la importación de la imagen se complete.
El siguiente código de muestra define e implementa un objeto SwingWorker.
Swingworker worker = new SwingWorker <imageiCon [], void> () {@Override public imageicon [] doInbackground () {final Imageicon [] innerimgs = new ImageIcon [NIMGS]; for (int i = 0; i <nimgs; i ++) {innerImgs [i] = loadImage (i+1); } return innerImgs; } @Override public void ded () {// Eliminar la etiqueta "Cargando imágenes". animator.removeall (); loopslot = -1; intente {imgs = get (); } capt (interruptedException ignore) {} catch (java.util.concurrent.executionException e) {string why = null; Causa lanzable = E.getCause (); if (causa! = null) {why = causa.getMessage (); } else {why = e.getMessage (); } System.err.println ("Error al recuperar el archivo:" + Why); }}}}; Todas las subclases heredadas de Swingworker deben implementar DoinBackground; La implementación del método hecho es opcional.
Tenga en cuenta que Swingworker es una clase de paradigma con dos parámetros. El primer parámetro de tipo especifica el tipo de retorno de DoinBackground. También es un tipo de método GET, que pueden llamarse otros hilos para obtener el valor de retorno de DoInbackground. El segundo parámetro de tipo especifica el tipo de resultado intermedio. Este ejemplo no devuelve el resultado intermedio, por lo que se establece en vacío.
Usando el método GET, puede hacer que la referencia al objeto IMGS (creado en el hilo del trabajador) se use en el hilo de despacho de eventos. Esto permite compartir objetos entre hilos.
En realidad, hay dos formas de obtener el objeto devuelto por la clase DoinBackground.
(1) No hay parámetros para llamar a swingworker.get. Si la tarea de fondo no se completa, el método GET se bloqueará hasta que se complete.
(2) Llame al swingworker. Obtenga parámetros para especificar el tiempo de espera. Si la tarea de fondo no se completa, bloquee hasta que se complete, a menos que expire el tiempo de espera, en cuyo caso obtendrá un java.util.concurrent.timeOutException.
5. Tareas con resultados intermedios
Es útil que una tarea de backend de trabajo proporcione resultados intermedios. Las tareas de backend pueden llamar al método swingworker.publish para hacer esto. Este método acepta muchos parámetros. Cada parámetro debe ser uno especificado por el segundo parámetro de tipo de swingworker.
Swingworker.process se puede anular para guardar los resultados proporcionados por el método de publicación. Este método es llamado por el hilo de despacho de eventos. El conjunto de resultados del método de publicación generalmente se recopila mediante un método de proceso.
Echemos un vistazo a los ejemplos proporcionados por filper.java. Este programa genera una serie de pruebas booleanas aleatorias java.util.random a través de una tarea de fondo. Es como un experimento de lanzamiento de monedas. Para informar sus resultados, la tarea de fondo usa una flippair de objeto.
Clase estática privada Flippair {Cabezas largas finales privadas, total; Flippair (cabezas largas, total largo) {this.heads = heads; this.total = total; }} Las cabezas representan el resultado de verdadero; El total representa el número total de lanzamientos.
El programa de antecedentes es una instancia de FilPTask:
clase privada FlipTask extiende Swingworker <Void, Flippair> {
Debido a que la tarea no devuelve un resultado final, no es necesario especificar cuál es el primer parámetro de tipo, use Void. Después de cada "lanzamiento", la tarea llama a publicar:
@OverrideProtected void doinBackground () {Long Heads = 0; Total largo = 0; Aleatorio aleatorio = new Random (); while (! iscancelled ()) {Total ++; if (random.nextboolean ()) {cabezales ++; } publicar (nuevo flpair (cabezas, total)); } return null;} Dado que a menudo se llama a la publicación, el hilo de despacho de eventos llamará a muchos valores de Flippair antes de que el hilo de despacho de eventos sea llamado; El proceso solo se centra en el último conjunto de valores devueltos cada vez y lo usa para actualizar la GUI:
Proceso vacío protegido (pares de listas) {par par de flpair = pars.get (par.size () - 1); headstext.settext (string.format ("%d", par.heads)); TotalText.settext (string.format ("%d", par.total)); devtext.setText (string.format ("%. 10g", ((doble) par.heads)/((doble) par.total) - 0.5));} 6. Cancelar la tarea de fondo
Llame a Swingworker.cancel para cancelar una tarea de fondo de ejecución. La tarea debe ser consistente con su propio mecanismo de deshacer. Hay dos formas de hacer esto:
(1) Cuando se recibe una interrupción, se terminará.
(2) Llame al swingworker.iscanceled. Si Swingworker llaman a las llamadas, el método devolverá verdadero.
7. Atar atributos y métodos de estado
Swingworker admite propiedades vinculadas, que es muy útil cuando se comunica con otros hilos. Proporciona dos propiedades de unión: progreso y estado. El progreso y el estado se pueden utilizar para activar tareas de procesamiento de eventos en hilos de envío de eventos.
Al implementar un oyente de cambio de propiedad, el programa puede detectar cambios en el progreso, el estado u otras propiedades vinculantes.
7.1 La variable unida al progreso
La variable de unión al progreso es una variable entera con un rango de 0 a 100. Predefinió el setter (el swingworker.setprogress) y los métodos de getter (el swingworker.getProgress).
7.2 La variable de estado unida
Los cambios en las variables de unión al estado reflejan el proceso de cambiar el objeto Swingworker durante su ciclo de vida. Esta variable contiene un tipo de enumeración de swingworker.stateValue. Los valores posibles son:
(1) pendiente
Este estado dura un tiempo para saber por la creación del objeto que se llama al método Doinbackground.
(2) Comenzó
Este estado dura un momento antes de que se llame al método DoInbackground hasta que se llame al método hecho.
(3) Hecho
El tiempo restante existe el objeto permanecerá en este estado.
Si necesita devolver el valor del estado actual, puede llamar a Swingworker.getState.
7.3 Métodos de Estado
Dos métodos proporcionados por la interfaz futura también pueden informar el estado de las tareas de fondo. Si la tarea se cancela, IsCancelled devuelve verdadero. Además, si la tarea se completa, es decir, se completa normalmente o se cancela, IsDone devuelve verdadero.
Usando un contenedor de nivel superior
Swing proporciona 3 clases de contenedores de nivel superior: Jframe, JDialog, Japplet. Al usar estas tres clases, debe prestar atención a los siguientes puntos:
(1). Para mostrarse en la pantalla, cada componente GUI debe ser parte de la jerarquía que contiene. La jerarquía de inclusión es una estructura de árbol del componente, y el contenedor de nivel superior es su raíz.
(2). Cada componente GUI solo se puede incluir una vez. Si un componente ya está en un contenedor y luego intenta agregarlo a un nuevo contenedor, el componente se eliminará del primer contenedor y se agregará al segundo contenedor.
(3). Cada contenedor de nivel superior tiene un panel de contenido. En general, este panel de contenido contendrá (directa o indirectamente) todos los componentes visuales de la GUI de contenedor de nivel superior.
(4). Puede agregar una barra de menú al contenedor superior. Por lo general, esta barra de menú se coloca en el contenedor superior, pero fuera del panel de contenido.
1. Contenedores de nivel superior y jerarquías de inclusión
Cada programa que utiliza el componente Swing tiene al menos un contenedor de nivel superior. Este contenedor de nivel superior es el nodo raíz que contiene la jerarquía: esta jerarquía contendrá todos los componentes de swing que aparecerán en este contenedor de nivel superior.
Por lo general, una aplicación basada en GUI swing separada tiene al menos una jerarquía de inclusión y su nodo raíz es un Jframe. Por ejemplo, si una aplicación tiene una ventana y dos cuadros de diálogo, la aplicación tendrá tres niveles de inclusión, es decir, habrá tres contenedores de nivel superior. Una jerarquía de contención toma Jframe como su nodo raíz, y otras dos jerarquías de contención tienen un jdialog como su nodo raíz.
Un appliñón basado en componentes de swing contiene al menos una jerarquía de inclusión, y se puede determinar que uno de ellos debe hacerse con un Japplet como nodo raíz. Por ejemplo, un applet con un cuadro de diálogo, tendrá dos niveles de inclusión. El componente en la ventana del navegador se colocará en una jerarquía de contención, y su nodo raíz es un objeto Japplet. El cuadro de diálogo tendrá una jerarquía que contiene, y su nodo raíz es un objeto jdialog.
2. Agregue componentes al panel de contenido
La siguiente operación del código es obtener el panel de contenido del cuadro en el ejemplo anterior y agregar una etiqueta amarilla:
Frame.getContentPane (). Add (YellowLabel, BorderLayout.Center);
Como se muestra en el código, primero debe encontrar el panel de contenido del contenedor de nivel superior e implementarlo a través del método GetContentPane. El panel de contenido predeterminado es un contenedor intermedio simple, que hereda de JComponent, utilizando un BorderLayout como su administrador de panel.
Personalizar un panel de contenido es simple: configure un administrador de panel o agregue bordes. Cabe señalar aquí que el método GetContentPane devolverá un objeto de contenedor, no un objeto JComponent. Esto significa que si necesita aprovechar parte de la funcionalidad del JComponente, también debe escribir convertir el valor de retorno o crear su propio componente como panel de contenido. Nuestro ejemplo generalmente usa el segundo método. Porque el segundo método es cada vez más claro. Otra forma en que a veces usamos es simplemente agregar un componente autodefinado al panel de contenido para cubrir completamente el panel de contenido.
Si crea su propio panel de contenido, tenga cuidado de asegurarse de que sea opaco. Un jpanel opaco será una buena opción. Tenga en cuenta que, de forma predeterminada, el diseño de JPanel se administra como FlowLayout, y es posible que desee reemplazarlo con otro administrador de diseño.
Para que un componente sea un panel de contenido, debe usar el método setContentPane del contenedor de nivel superior, por ejemplo:
// Cree un panel y agregue componentes a It.jpanel ContentPane = new JPanel (new BorderLayout ()); ContentPane.SetBorder (SomeBorder); ContentPane.Add (Somecomponent, BorderLayout.Center); ContentPane.Add (OtroComponent, BorderLayout.page_end);//hacer que el contenido sea el contenido del contenido panele.//contentpane.setOpaque(true) ;TopLevelContainer.setContentPane(ContentPane);
Nota: No use contenedores transparentes como paneles de contenido, como JSCrollPane, JSplitPane y JTabbedPane. Un panel de contenido transparente hará que los componentes se confundan. Aunque puede hacer que cualquier componente de swing transparente sea opaco a través del método SetOpAque (verdadero), no se verá bien cuando algunos componentes estén completamente opacos. Por ejemplo, un panel de etiqueta.
3. Agregar una barra de menú
En teoría, cada contenedor de nivel superior puede tener una barra de menú. Pero los hechos muestran que la barra de menú solo aparece en Frame o Applet. Para agregar una barra de menú al contenedor de nivel superior, debe crear un objeto JMenubar, ensamblar algunos menús y luego llamar al método SetJMenubar. La instancia de ToplevelDemo agrega una barra de menú a su marco a través del siguiente código.
Frame.SetJMenubar (CyanMenubar);
4. El panel de raíz
Cada contenedor de nivel superior se basa en un contenedor intermedio implícito llamado contenedor de raíz. Este contenedor de raíz gestiona el panel de contenido y la barra de menú, junto con dos o más contenedores (ver panel en capas, etc.). Por lo general, no necesita saber sobre el uso del contenedor de raíz del componente Swing. Sin embargo, si desea interceptar un clic del mouse o dibujar en múltiples componentes, debe conocer el contenedor de raíz.
Lo anterior se ha descrito sobre el panel de contenido y la barra de menú opcional, y no se repetirá aquí. Los otros dos componentes contenidos en el contenedor de raíz son el panel de diseño y el panel de vidrio. El panel de diseño contiene directamente el panel de menú y el panel de contenido, y le permite ordenar los otros componentes agregados por las coordenadas Z. Los paneles de vidrio generalmente se usan para interceptar acciones de entrada que ocurren en la capa superior y también se pueden usar para dibujar en múltiples componentes.