Cómo cerrar una aplicación Java Swing desde el código

94

¿Cuál es la forma correcta de terminar una aplicación Swing desde el código y cuáles son las trampas?

Intenté cerrar mi aplicación automáticamente después de que se activara un temporizador. Pero simplemente llamar dispose()al JFrameno funcionó: la ventana desapareció pero la aplicación no se cerró. Sin embargo, al cerrar la ventana con el botón cerrar, la aplicación finaliza. ¿Qué tengo que hacer?

hstoerr
fuente
Publique un fragmento de código de su temporizador.
James Schek

Respuestas:

106

Su acción de cierre predeterminada de JFrame se puede establecer en " DISPOSE_ON_CLOSE" en lugar de EXIT_ON_CLOSE(no entiendo por qué la gente sigue usando EXIT_ON_CLOSE).

Si tiene ventanas no desechadas o subprocesos que no son demonios, su aplicación no terminará. Esto debería considerarse un error (y solucionarlo con System.exit es una muy mala idea).

Los culpables más comunes son java.util.Timer y un hilo personalizado que ha creado. Ambos deben establecerse en demonio o deben eliminarse explícitamente.

Si desea verificar todos los marcos activos, puede usar Frame.getFrames(). Si se eliminan todos los Windows / Frames, utilice un depurador para comprobar si hay subprocesos que no sean demonios y que aún se estén ejecutando.

James Schek
fuente
En mi caso descubrí con el depurador que tenía un Swingworker todavía activo. Llamé a su método cancel (true) en WindowClose Eventlistener y el programa termina ahora. ¡Gracias!
Hans-Peter Störr
Tenga en cuenta que puede que tenga que llamar a dispose en cada marco, y normalmente eso es "suficiente" (aunque establecer la acción de cierre predeterminada en EXIT_ON_CLOSE probablemente tampoco sea una mala idea).
rogerdpack
¿Qué sucede si tiene una ventana que se abre a otra, pero no se deshace sola, de modo que pueda usarla como backventana? Si la segunda ventana se cierra y usas, DISPOSE_ON_CLOSEel programa no termina porque la primera ventana aún está "sin eliminar" ... ¿hay alguna manera de resolver eso sin usar DISPOSE_ON_CLOSE?
Svend Hansen
La primera ventana debe agregarse a sí misma como oyente a la segunda ventana y tomar la acción apropiada.
James Schek
3
El uso de EXIT_ON_CLOSE terminará forzosamente la aplicación. Si necesita tomar medidas antes de que salga el programa (como guardar datos de trabajo), debe acceder a los controladores de eventos de JVM en lugar de utilizar los controladores de eventos de swing normales. Ambos "funcionarán", pero EXIT_ON_CLOSE causará problemas para aplicaciones más complejas.
James Schek
34

Supongo que EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

antes System.exit(0)es mejor, ya que puede escribir un escucha de ventana para realizar algunas operaciones de limpieza antes de salir de la aplicación.

Ese oyente de ventana le permite definir:

public void windowClosing(WindowEvent e) {
    displayMessage("WindowListener method called: windowClosing.");
    //A pause so user can see the message before
    //the window actually closes.
    ActionListener task = new ActionListener() {
        boolean alreadyDisposed = false;
        public void actionPerformed(ActionEvent e) {
            if (frame.isDisplayable()) {
                alreadyDisposed = true;
                frame.dispose();
            }
        }
    };
    Timer timer = new Timer(500, task); //fire every half second
    timer.setInitialDelay(2000);        //first delay 2 seconds
    timer.setRepeats(false);
    timer.start();
}

public void windowClosed(WindowEvent e) {
    //This will only be seen on standard output.
    displayMessage("WindowListener method called: windowClosed.");
}
VonC
fuente
16

Tratar:

System.exit(0);

Crudo, pero efectivo.

Daniel Spiewak
fuente
Desafortunadamente, esto es demasiado crudo para mí. Quiero que los eventos de cierre de la ventana se procesen para algunas acciones de limpieza. Bien, podría hacer un System.exit con un SwingUtils.invokeLater, pero prefiero hacer lo correcto.
Hans-Peter Störr
5
System.exit (0) no es solo crudo, es malvado. Compruebe todos los marcos, llame a disponer. Si eso falla, ejecute un depurador y vea qué subprocesos que no son demonios aún están activos.
James Schek
Ha habido muchos errores incluso dentro del JDK que dejan el proceso. Entonces +1 para salir (), pero es posible que desee deshabilitarlo en las compilaciones de depuración.
Tom Hawtin - tackline
11

Puede que la forma segura sea algo como:

    private JButton btnExit;
    ...
    btnExit = new JButton("Quit");      
    btnExit.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e){
            Container frame = btnExit.getParent();
            do 
                frame = frame.getParent(); 
            while (!(frame instanceof JFrame));                                      
            ((JFrame) frame).dispose();
        }
    });
Kachwahed
fuente
3
Bastante mal estilo para nombrar una variable Framecon una letra mayúscula, exactamente como el nombre de una clase…
Jean-Philippe Pellet
Muchas gracias! ¡Esta es una de las mejores formas!
rzaaeeff
9

El siguiente programa incluye código que terminará un programa que carece de subprocesos extraños sin llamar explícitamente a System.exit (). Para aplicar este ejemplo a aplicaciones que usan hilos / oyentes / temporizadores / etc., solo es necesario insertar el código de limpieza solicitando (y, si corresponde, esperando) su terminación antes de que WindowEvent se inicie manualmente dentro de actionPerformed ().

Para aquellos que deseen copiar / pegar código capaz de ejecutarse exactamente como se muestra, al final se incluye un método principal un poco feo pero por lo demás irrelevante.

public class CloseExample extends JFrame implements ActionListener {

    private JButton turnOffButton;

    private void addStuff() {
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        turnOffButton = new JButton("Exit");
        turnOffButton.addActionListener(this);
        this.add(turnOffButton);
    }

    public void actionPerformed(ActionEvent quitEvent) {
        /* Iterate through and close all timers, threads, etc here */
        this.processWindowEvent(
                new WindowEvent(
                      this, WindowEvent.WINDOW_CLOSING));
    }

    public CloseExample() {
        super("Close Me!");
        addStuff();
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                CloseExample cTW = new CloseExample();
                cTW.setSize(200, 100);
                cTW.setLocation(300,300);
                cTW.setVisible(true);
            }
        });
    }
}
Eric Sadler
fuente
4

Si le entiendo correctamente, desea cerrar la aplicación incluso si el usuario no hizo clic en el botón cerrar. Deberá registrar WindowEvents tal vez con addWindowListener () o enableEvents (), lo que mejor se adapte a sus necesidades.

Luego, puede invocar el evento con una llamada a processWindowEvent (). Aquí hay un código de muestra que creará un JFrame, esperará 5 segundos y cerrará el JFrame sin interacción del usuario.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class ClosingFrame extends JFrame implements WindowListener{

public ClosingFrame(){
    super("A Frame");
    setSize(400, 400);
            //in case the user closes the window
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setVisible(true);
            //enables Window Events on this Component
            this.addWindowListener(this);

            //start a timer
    Thread t = new Timer();
            t.start();
    }

public void windowOpened(WindowEvent e){}
public void windowClosing(WindowEvent e){}

    //the event that we are interested in
public void windowClosed(WindowEvent e){
    System.exit(0);
}

public void windowIconified(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowActivated(WindowEvent e){}
public void windowDeactivated(WindowEvent e){}

    //a simple timer 
    class Timer extends Thread{
           int time = 10;
           public void run(){
     while(time-- > 0){
       System.out.println("Still Waiting:" + time);
               try{
                 sleep(500);                     
               }catch(InterruptedException e){}
             }
             System.out.println("About to close");
    //close the frame
            ClosingFrame.this.processWindowEvent(
                 new WindowEvent(
                       ClosingFrame.this, WindowEvent.WINDOW_CLOSED));
           }
    }

    //instantiate the Frame
public static void main(String args[]){
          new ClosingFrame();
    }

}

Como puede ver, el método processWindowEvent () hace que se active el evento WindowClosed donde tiene la oportunidad de hacer algo de código de limpieza si lo necesita antes de cerrar la aplicación.

Vincent Ramdhanie
fuente
windowClosed debe llamar a dispose () no a System.exit (0).
James Schek
2

Eche un vistazo a la documentación de Oracle .

A partir de JDK 1.4, una aplicación termina si:

  • No hay componentes AWT o Swing visualizables.
  • No hay eventos nativos en la cola de eventos nativos.
  • No hay eventos AWT en Java EventQueues.

Esquineros:

El documento establece que algunos paquetes crean componentes que se pueden visualizar sin liberarlos. Un programa que llame a Toolkit.getDefaultToolkit () no terminará. se da, entre otros, como ejemplo.

Además, otros procesos pueden mantener vivo a AWT cuando, por cualquier motivo, envían eventos a la cola de eventos nativa.

También noté que en algunos sistemas se tarda un par de segundos antes de que la aplicación finalice.

Reiner Lange
fuente
0

Creo que la idea está aquí en WindowListener: puede agregar cualquier código allí que le gustaría ejecutar antes de que se apague.

Tamas Rev
fuente
0

En respuesta a otros comentarios, DISPOSE_ON_CLOSE no parece salir correctamente de la aplicación; solo destruye la ventana, pero la aplicación seguirá ejecutándose. Si desea terminar la aplicación, use EXIT_ON_CLOSE.

JSideris
fuente
Esta respuesta sugiere exactamente lo contrario de la respuesta de James Schek. Cual es la correcta? (cuando pruebo con una aplicación simple, ambos parecen funcionar ...)
OR Mapper
No recuerdo cómo probé esto ahora.
JSideris