Come chiudere un'applicazione Java Swing dal codice


94

Qual è il modo corretto per terminare un'applicazione Swing dal codice e quali sono le insidie?

Ho provato a chiudere automaticamente la mia applicazione dopo che un timer si è attivato. Ma solo chiamare dispose()il JFramenon ha funzionato: la finestra è scomparsa ma l'applicazione non è stata chiusa. Tuttavia, quando si chiude la finestra con il pulsante di chiusura, l'applicazione viene chiusa. Cosa dovrei fare?


Pubblica uno snippet di codice del tuo timer.
James Schek

Risposte:


106

La tua azione di chiusura predefinita di JFrame può essere impostata su " DISPOSE_ON_CLOSE" anziché su EXIT_ON_CLOSE(il motivo per cui le persone continuano a utilizzare EXIT_ON_CLOSE va oltre le mie capacità).

Se si dispone di finestre non visualizzate o thread non daemon, l'applicazione non verrà terminata. Questo dovrebbe essere considerato un errore (e risolverlo con System.exit è una pessima idea).

I colpevoli più comuni sono java.util.Timer e un thread personalizzato che hai creato. Entrambi dovrebbero essere impostati su daemon o devono essere uccisi esplicitamente.

Se vuoi controllare tutti i frame attivi, puoi usare Frame.getFrames(). Se tutte le finestre / i frame vengono eliminati, utilizzare un debugger per verificare la presenza di thread non daemon ancora in esecuzione.


Nel mio caso ho scoperto con il debugger che avevo ancora uno Swingworker attivo. Ho chiamato il suo metodo cancel (true) nel WindowClose Eventlistener e il programma termina ora. Grazie!
Hans-Peter Störr

Tieni presente che potresti dover chiamare dispose su ogni frame e in genere è "sufficiente" (anche se l'impostazione dell'azione di chiusura predefinita su EXIT_ON_CLOSE probabilmente non è nemmeno una cattiva idea).
Rogerdpack

Cosa succede se una finestra si apre su un'altra, ma non si dispone da sola, in modo da poterla utilizzare per una backfinestra? Se poi si chiude la seconda finestra e si utilizza DISPOSE_ON_CLOSEil programma non si chiude perché la prima finestra è ancora "non disponibile" ... c'è un modo per risolverlo senza usare DISPOSE_ON_CLOSE?
Svend Hansen

La prima finestra dovrebbe aggiungersi come ascoltatore alla seconda finestra e intraprendere l'azione appropriata.
James Schek

3
L'utilizzo di EXIT_ON_CLOSE terminerà forzatamente l'applicazione. Se è necessario eseguire un'azione prima che il programma esca (come il salvataggio dei dati di lavoro), è necessario attingere ai gestori di eventi JVM invece di utilizzare solo i normali gestori di eventi swing. Entrambi "funzioneranno", ma EXIT_ON_CLOSE causerà problemi ad app più complesse.
James Schek,

34

Immagino che EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

prima System.exit(0)è meglio dato che puoi scrivere un Listener di finestre per eseguire alcune operazioni di pulizia prima di lasciare effettivamente l'app.

Quel listener di finestre ti consente di definire:

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.");
}

16

Provare:

System.exit(0);

Grezzo, ma efficace.


Sfortunatamente questo è troppo rozzo per me. Voglio che gli eventi di chiusura della finestra vengano elaborati per alcune azioni di pulizia. OK, potrei fare un System.exit con SwingUtils.invokeLater, ma preferisco fare la cosa giusta.
Hans-Peter Störr

5
System.exit (0) non è solo rozzo, è malvagio. Controlla tutti i frame, chiama dispose. Se fallisce, esegui un debugger e guarda quali thread non daemon sono ancora attivi.
James Schek

Ci sono stati molti bug anche all'interno del JDK che lasciano il processo. Quindi fai +1 per uscire (), ma potresti volerlo disabilitare nelle build di debug.
Tom Hawtin - tackline

11

Può essere il modo sicuro è qualcosa del tipo:

    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();
        }
    });

3
Stile piuttosto brutto per nominare una variabile Framecon una lettera maiuscola, esattamente come il nome di una classe ...
Jean-Philippe Pellet

Grazie mille! Questo è uno dei modi migliori!
rzaaeeff

9

Il seguente programma include codice che terminerà un programma privo di thread estranei senza chiamare esplicitamente System.exit (). Per applicare questo esempio alle applicazioni che utilizzano thread / listener / timer / ecc., È sufficiente inserire il codice di pulizia che richiede (e, se applicabile, in attesa) la loro terminazione prima che WindowEvent venga avviato manualmente all'interno di actionPerformed ().

Per coloro che desiderano copiare / incollare codice in grado di funzionare esattamente come mostrato, alla fine è incluso un metodo principale leggermente brutto ma per il resto irrilevante.

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);
            }
        });
    }
}

4

Se ti ho capito bene, vuoi chiudere l'applicazione anche se l'utente non ha cliccato sul pulsante di chiusura. Dovrai registrare WindowEvents magari con addWindowListener () o enableEvents () a seconda di quale si adatta meglio alle tue esigenze.

È quindi possibile richiamare l'evento con una chiamata a processWindowEvent (). Ecco un codice di esempio che creerà un JFrame, attenderà 5 secondi e chiuderà il JFrame senza l'interazione dell'utente.

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();
    }

}

Come puoi vedere, il metodo processWindowEvent () provoca l'attivazione dell'evento WindowClosed in cui hai l'opportunità di eseguire un po 'di pulizia del codice se necessario prima di chiudere l'applicazione.


windowClosed dovrebbe chiamare dispose () e non System.exit (0).
James Schek

2

Dai un'occhiata alla documentazione Oracle .

A partire da JDK 1.4 un'applicazione termina se:

  • Non ci sono componenti AWT o Swing visualizzabili.
  • Non sono presenti eventi nativi nella coda degli eventi nativi.
  • Non ci sono eventi AWT in java EventQueues.

Cornici:

Il documento afferma che alcuni pacchetti creano componenti visualizzabili senza rilasciarli. Un programma che chiama Toolkit.getDefaultToolkit () non terminerà. è tra l'altro dato come esempio.

Anche altri processi possono mantenere attivo AWT quando, per qualsiasi motivo, inviano eventi nella coda degli eventi nativi.

Inoltre ho notato che su alcuni sistemi ci vogliono un paio di secondi prima che l'applicazione termini effettivamente.


0

Penso che l'idea sia qui il WindowListener: puoi aggiungere qualsiasi codice che desideri eseguire prima che la cosa si spenga


0

In risposta ad altri commenti, DISPOSE_ON_CLOSE non sembra uscire correttamente dall'applicazione: distrugge solo la finestra, ma l'applicazione continuerà a funzionare. Se desideri terminare l'applicazione, utilizza EXIT_ON_CLOSE.


Questa risposta suggerisce l'esatto opposto della risposta di James Schek. Quale è corretto? (quando provo con una semplice applicazione, entrambi sembrano funzionare ...)
OPPURE Mapper

Non riesco a ricordare come l'ho testato ora.
JSideris
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.