Jak zamknąć aplikację Java Swing z kodu

94

Jaki jest właściwy sposób usunięcia aplikacji Swing z kodu i jakie są pułapki?

Próbowałem automatycznie zamknąć aplikację po uruchomieniu licznika czasu. Ale po prostu dzwoniąc dispose()na JFramenie załatwi - okno zniknęło ale aplikacja nie zakończy. Jednak zamykanie okna przyciskiem zamykania powoduje zakończenie działania aplikacji. Co powinienem zrobić?

hstoerr
źródło
Prześlij fragment kodu swojego timera.
James Schek

Odpowiedzi:

106

Domyślną akcję zamykania JFrame można ustawić na „ DISPOSE_ON_CLOSE” zamiast EXIT_ON_CLOSE(dlaczego ludzie nadal używają EXIT_ON_CLOSE jest poza mną).

Jeśli masz jakieś niewyświetlone okna lub wątki niebędące demonami, Twoja aplikacja nie zostanie zakończona. Należy to uznać za błąd (a rozwiązanie go za pomocą System.exit to bardzo zły pomysł).

Najczęstszymi winowajcami są java.util.Timer i utworzony przez Ciebie niestandardowy wątek. Oba powinny być ustawione na demona lub jawnie zabite.

Jeśli chcesz sprawdzić wszystkie aktywne ramki, możesz użyć Frame.getFrames(). Jeśli wszystkie okna / ramki zostaną usunięte, użyj debugera, aby sprawdzić, czy nie działają nadal wątki niebędące demonami.

James Schek
źródło
W moim przypadku odkryłem za pomocą debugera, że ​​mam nadal aktywny Swingworker. Nazwałem jej metodę cancel (true) w WindowClose Eventlistener i program kończy się teraz. Dzięki!
Hans-Peter Störr
Zauważ, że być może będziesz musiał wywołać dispose dla każdej klatki i zazwyczaj jest to „wystarczające” (chociaż ustawienie domyślnej akcji close na EXIT_ON_CLOSE też prawdopodobnie nie jest złym pomysłem).
rogerdpack
A co, jeśli jedno okno otwiera inne, ale się nie usuwa, aby można było użyć go jako backokna? Jeśli drugie okno zostanie następnie zamknięte, a DISPOSE_ON_CLOSEprogram nie zostanie zakończony, ponieważ pierwsze okno jest nadal „niewykorzystane” ... czy istnieje sposób na rozwiązanie tego problemu bez użycia DISPOSE_ON_CLOSE?
Svend Hansen
Pierwsze okno powinno dodać się jako słuchacz do drugiego okna i podjąć odpowiednią akcję.
James Schek,
3
Użycie EXIT_ON_CLOSE spowoduje wymuszenie zakończenia aplikacji. Jeśli musisz wykonać działanie przed zamknięciem programu (na przykład zapisać dane robocze), musisz skorzystać z programów obsługi zdarzeń JVM, zamiast używać zwykłych programów obsługi zdarzeń swing. Oba będą działać, ale EXIT_ON_CLOSE będzie powodować problemy w przypadku bardziej złożonych aplikacji.
James Schek
34

Myślę, że EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

wcześniej System.exit(0)jest lepsze, ponieważ możesz napisać odbiornik okien, aby wykonać pewne operacje czyszczenia przed faktycznym opuszczeniem aplikacji.

Ten detektor okien umożliwia zdefiniowanie:

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
źródło
16

Próbować:

System.exit(0);

Surowy, ale skuteczny.

Daniel Śpiewak
źródło
Niestety jest to dla mnie zbyt surowe. Chcę, aby zdarzenia zamknięcia okna były przetwarzane dla niektórych akcji czyszczenia. OK, mógłbym zrobić System.exit za pomocą SwingUtils.invokeLater, ale wolałbym zrobić właściwą rzecz.
Hans-Peter Störr
5
System.exit (0) jest nie tylko surowy, ale także zły. Sprawdź wszystkie ramki, zadzwoń do dispose. Jeśli to się nie powiedzie, uruchom debugger i zobacz, które wątki nie będące demonami nadal działają.
James Schek
Nawet w JDK było wiele błędów, które pomijają cały proces. Więc +1, aby exit (), ale możesz chcieć wyłączyć to w kompilacjach debugowania.
Tom Hawtin - tackline
11

Może być bezpiecznym sposobem:

    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
źródło
3
Całkiem zły styl nazywania zmiennej Framewielką literą, dokładnie tak, jak nazwa klasy…
Jean-Philippe Pellet
Dziękuję bardzo! To jeden z najlepszych sposobów!
rzaaeeff
9

Poniższy program zawiera kod, który zakończy działanie programu bez dodatkowych wątków bez jawnego wywołania System.exit (). Aby zastosować ten przykład do aplikacji używających wątków / nasłuchiwaczy / timerów / itp., Wystarczy wstawić kod czyszczenia żądający (i, jeśli ma to zastosowanie, oczekiwanie) na ich zakończenie, zanim WindowEvent zostanie ręcznie zainicjowany w actionPerformed ().

Dla tych, którzy chcą skopiować / wkleić kod działający dokładnie tak, jak pokazano, na końcu dołączona jest nieco brzydka, ale poza tym nieistotna główna metoda.

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
źródło
4

Jeśli dobrze cię rozumiem, chcesz zamknąć aplikację, nawet jeśli użytkownik nie kliknął przycisku zamykania. Będziesz musiał zarejestrować WindowEvents, być może za pomocą addWindowListener () lub enableEvents (), w zależności od tego, co bardziej odpowiada Twoim potrzebom.

Następnie możesz wywołać zdarzenie za pomocą wywołania processWindowEvent (). Oto przykładowy kod, który utworzy JFrame, odczekaj 5 sekund i zamknie JFrame bez interakcji użytkownika.

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

}

Jak widać, metoda processWindowEvent () powoduje wywołanie zdarzenia WindowClosed, w którym istnieje możliwość wyczyszczenia kodu przed zamknięciem aplikacji.

Vincent Ramdhanie
źródło
windowClosed powinno wywoływać dispose (), a nie System.exit (0).
James Schek
2

Zapoznaj się z dokumentacją Oracle .

Począwszy od JDK 1.4, aplikacja kończy działanie, jeśli:

  • Nie ma wyświetlanych komponentów AWT lub Swing.
  • W rodzimej kolejce zdarzeń nie ma zdarzeń natywnych.
  • W java EventQueues nie ma wydarzeń AWT.

Narożniki:

Dokument stwierdza, że ​​niektóre pakiety tworzą komponenty, które można wyświetlać, bez ich zwalniania. Program, który wywołuje Toolkit.getDefaultToolkit (), nie zakończy działania. jest między innymi podany jako przykład.

Również inne Procesy mogą utrzymywać AWT przy życiu, gdy z jakiegokolwiek powodu wysyłają zdarzenia do natywnej kolejki zdarzeń.

Zauważyłem również, że w niektórych systemach potrzeba kilku sekund, zanim aplikacja faktycznie się zakończy.

Reiner Lange
źródło
0

Myślę, że idea jest tutaj WindowListener - możesz dodać tam dowolny kod, który chciałbyś uruchomić, zanim coś się wyłączy

Tamas Rev
źródło
0

W odpowiedzi na inne komentarze, DISPOSE_ON_CLOSE nie wydaje się prawidłowo wychodzić z aplikacji - niszczy tylko okno, ale aplikacja będzie nadal działać. Jeśli chcesz zamknąć aplikację, użyj EXIT_ON_CLOSE.

JSideris
źródło
Ta odpowiedź sugeruje dokładne przeciwieństwo odpowiedzi Jamesa Scheka. Który jest prawidłowy? (kiedy testuję za pomocą prostej aplikacji, obie wydają się działać ...)
LUB Mapper
Nie pamiętam, jak teraz to testowałem.
JSideris