So schließen Sie eine Java Swing-Anwendung aus dem Code

94

Was ist der richtige Weg, um eine Swing-Anwendung aus dem Code zu beenden, und was sind die Fallstricke?

Ich habe versucht, meine Anwendung automatisch zu schließen, nachdem ein Timer ausgelöst wurde. Aber nur das Aufrufen dispose()des JFramehat nicht geholfen - das Fenster verschwand, aber die Anwendung wurde nicht beendet. Wenn Sie das Fenster jedoch mit der Schaltfläche Schließen schließen, wird die Anwendung beendet. Was soll ich machen?

hstoerr
quelle
Bitte posten Sie einen Code-Ausschnitt Ihres Timers.
James Schek

Antworten:

106

Ihre JFrame-Standardschließaktion kann auf " DISPOSE_ON_CLOSE" anstatt "gesetzt werden EXIT_ON_CLOSE(warum Benutzer weiterhin EXIT_ON_CLOSE verwenden, ist mir ein Rätsel).

Wenn Sie nicht bereitgestellte Fenster oder Nicht-Daemon-Threads haben, wird Ihre Anwendung nicht beendet. Dies sollte als Fehler angesehen werden (und das Lösen mit System.exit ist eine sehr schlechte Idee).

Die häufigsten Schuldigen sind java.util.Timer und ein benutzerdefinierter Thread, den Sie erstellt haben. Beide sollten auf Daemon gesetzt sein oder müssen explizit beendet werden.

Wenn Sie nach allen aktiven Frames suchen möchten, können Sie verwenden Frame.getFrames(). Wenn alle Windows / Frames entsorgt sind, überprüfen Sie mit einem Debugger, ob noch Nicht-Daemon-Threads ausgeführt werden.

James Schek
quelle
In meinem Fall stellte ich mit dem Debugger fest, dass noch ein Swingworker aktiv war. Ich habe die Methode cancel (true) im WindowClose Eventlistener aufgerufen und das Programm wird jetzt beendet. Vielen Dank!
Hans-Peter Störr
Beachten Sie, dass Sie möglicherweise dispose für jeden Frame aufrufen müssen. Dies ist normalerweise "ausreichend" (obwohl das Festlegen der Standardschließaktion auf EXIT_ON_CLOSE wahrscheinlich auch keine schlechte Idee ist).
Rogerdpack
Was ist, wenn ein Fenster ein anderes öffnet, sich aber nicht selbst entsorgt, damit Sie es für ein backFenster verwenden können? Wenn das zweite Fenster dann geschlossen wird und Sie DISPOSE_ON_CLOSEdas Programm verwenden, wird es nicht beendet, da das erste Fenster immer noch "nicht verfügbar" ist. Gibt es eine Möglichkeit, dies ohne Verwendung zu lösen DISPOSE_ON_CLOSE?
Svend Hansen
Das erste Fenster sollte sich als Listener zum zweiten Fenster hinzufügen und entsprechende Maßnahmen ergreifen.
James Schek
3
Durch die Verwendung von EXIT_ON_CLOSE wird die Anwendung zwangsweise beendet. Wenn Sie vor dem Beenden des Programms Maßnahmen ergreifen müssen (z. B. Speichern von Arbeitsdaten), müssen Sie auf die JVM-Ereignishandler zugreifen, anstatt nur die normalen Swing-Ereignishandler zu verwenden. Beide "funktionieren", aber EXIT_ON_CLOSE verursacht Probleme bei komplexeren Apps.
James Schek
34

Ich denke ein EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

Vorher System.exit(0)ist besser, da Sie einen Fenster-Listener schreiben können, um einige Reinigungsvorgänge durchzuführen, bevor Sie die App tatsächlich verlassen.

Mit diesem Fenster-Listener können Sie Folgendes definieren:

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
quelle
16

Versuchen:

System.exit(0);

Roh, aber effektiv.

Daniel Spiewak
quelle
Leider ist mir das zu grob. Ich möchte, dass die Fensterschließereignisse für einige Bereinigungsaktionen verarbeitet werden. OK, ich könnte ein System.exit mit einem SwingUtils.invokeLater machen, aber ich würde lieber das Richtige tun.
Hans-Peter Störr
5
System.exit (0) ist nicht nur grob, es ist böse. Überprüfen Sie alle Frames, rufen Sie dispose auf. Wenn dies fehlschlägt, führen Sie einen Debugger aus und prüfen Sie, welche Nicht-Daemon-Threads noch aktiv sind.
James Schek
Es gab sogar innerhalb des JDK viele Fehler, die den Prozess verlassen. Also +1, um () zu beenden, aber Sie möchten es möglicherweise in Debug-Builds deaktivieren.
Tom Hawtin - Tackline
11

Vielleicht ist der sichere Weg so etwas wie:

    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
quelle
3
Ziemlich schlechter Stil, um eine Variable Framemit einem Großbuchstaben zu benennen , genau wie der Name einer Klasse…
Jean-Philippe Pellet
Ich danke dir sehr! Dies ist einer der besten Wege!
Rzaaeeff
9

Das folgende Programm enthält Code, der ein Programm ohne fremde Threads beendet, ohne System.exit () explizit aufzurufen. Um dieses Beispiel auf Anwendungen anzuwenden, die Threads / Listener / Timer / usw. verwenden, muss nur ein Bereinigungscode eingefügt werden, der ihre Beendigung anfordert (und gegebenenfalls abwartet), bevor das WindowEvent manuell in actionPerformed () initiiert wird.

Für diejenigen, die Code kopieren / einfügen möchten, der genau wie gezeigt ausgeführt werden kann, ist am Ende eine leicht hässliche, aber ansonsten irrelevante Hauptmethode enthalten.

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
quelle
4

Wenn ich Sie richtig verstehe, möchten Sie die Anwendung schließen, auch wenn der Benutzer nicht auf die Schaltfläche Schließen geklickt hat. Sie müssen WindowEvents möglicherweise bei addWindowListener () oder enableEvents () registrieren, je nachdem, was Ihren Anforderungen besser entspricht.

Sie können das Ereignis dann mit einem Aufruf von processWindowEvent () aufrufen. Hier ist ein Beispielcode, der einen JFrame erstellt, 5 Sekunden wartet und den JFrame ohne Benutzerinteraktion schließt.

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

}

Wie Sie sehen können, bewirkt die processWindowEvent () -Methode, dass das WindowClosed-Ereignis ausgelöst wird, wenn Sie vor dem Schließen der Anwendung die Möglichkeit haben, bei Bedarf Bereinigungscode auszuführen.

Vincent Ramdhanie
quelle
windowClosed sollte dispose () und nicht System.exit (0) aufrufen.
James Schek
2

Schauen Sie sich die Oracle-Dokumentation an .

Ab JDK 1.4 wird eine Anwendung beendet, wenn:

  • Es sind keine AWT- oder Swing-Komponenten sichtbar.
  • In der Warteschlange für native Ereignisse befinden sich keine nativen Ereignisse.
  • In Java EventQueues gibt es keine AWT-Ereignisse.

Eckkästen:

Das Dokument besagt, dass einige Pakete anzeigbare Komponenten erstellen, ohne sie freizugeben. Ein Programm, das Toolkit.getDefaultToolkit () aufruft, wird nicht beendet. wird unter anderem als Beispiel gegeben.

Auch andere Prozesse können AWT am Leben erhalten, wenn sie aus irgendeinem Grund Ereignisse in die native Ereigniswarteschlange senden.

Außerdem habe ich festgestellt, dass es auf einigen Systemen einige Sekunden dauert, bis die Anwendung tatsächlich beendet wird.

Reiner Lange
quelle
0

Ich denke, die Idee ist hier der WindowListener - Sie können dort jeden Code hinzufügen, den Sie ausführen möchten, bevor das Ding heruntergefahren wird

Tamas Rev.
quelle
0

In Reaktion auf andere Kommentare scheint DISPOSE_ON_CLOSE die Anwendung nicht ordnungsgemäß zu beenden - es zerstört nur das Fenster, aber die Anwendung wird weiterhin ausgeführt. Wenn Sie die Anwendung beenden möchten, verwenden Sie EXIT_ON_CLOSE.

JSideris
quelle
Diese Antwort legt das genaue Gegenteil von James Scheks Antwort nahe. Welches ist korrekt? (Wenn ich mit einer einfachen Anwendung teste, scheinen beide zu funktionieren ...)
ODER Mapper
Ich kann mich nicht erinnern, wie ich das jetzt getestet habe.
JSideris