Como fechar um aplicativo Java Swing a partir do código

94

Qual é a maneira correta de encerrar um aplicativo Swing do código e quais são as armadilhas?

Tentei fechar meu aplicativo automaticamente depois que um cronômetro disparou. Mas apenas chamar dispose()o JFramenão funcionou - a janela desapareceu, mas o aplicativo não foi encerrado. No entanto, ao fechar a janela com o botão Fechar, o aplicativo é encerrado. O que devo fazer?

hstoerr
fonte
Publique um snippet de código do seu cronômetro.
James Schek

Respostas:

106

A ação de fechamento padrão do JFrame pode ser definida como " DISPOSE_ON_CLOSE" em vez de EXIT_ON_CLOSE(por que as pessoas continuam usando EXIT_ON_CLOSE está além da minha compreensão).

Se você tiver janelas não dispostas ou threads não daemon, seu aplicativo não será encerrado. Isso deve ser considerado um erro (e resolvê-lo com System.exit é uma ideia muito ruim).

Os culpados mais comuns são java.util.Timer e um Thread personalizado que você criou. Ambos devem ser configurados para daemon ou explicitamente eliminados.

Se você quiser verificar todos os quadros ativos, pode usar Frame.getFrames(). Se todos os Windows / Frames forem descartados, use um depurador para verificar se há threads que não sejam daemon que ainda estão em execução.

James Schek
fonte
No meu caso, descobri com o depurador que ainda tinha um Swingworker ativo. Chamei seu método cancel (true) no Eventlistener WindowClose e o programa termina agora. Obrigado!
Hans-Peter Störr
Observe que você pode ter que chamar dispose em cada quadro, e normalmente isso é "suficiente" (embora definir a ação de fechamento padrão para EXIT_ON_CLOSE provavelmente não seja uma má ideia também).
rogerdpack de
E se você tiver uma janela abrindo a outra, mas não se desfazendo, de forma que você possa usá-la como backjanela? Se a segunda janela for fechada e você usar DISPOSE_ON_CLOSEo programa não termina porque a primeira janela ainda está "não-disposta" ... há uma maneira de resolver isso sem usar DISPOSE_ON_CLOSE?
Svend Hansen
A primeira janela deve se adicionar como ouvinte à segunda janela e executar as ações apropriadas.
James Schek de
3
Usar EXIT_ON_CLOSE forçará o encerramento do aplicativo. Se você precisar executar uma ação antes de encerrar o programa (como salvar dados de trabalho), será necessário acessar os manipuladores de eventos JVM em vez de apenas usar os manipuladores normais de eventos swing. Ambos "funcionarão", mas EXIT_ON_CLOSE causará problemas para aplicativos mais complexos.
James Schek
34

Eu acho um EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

antes System.exit(0)é melhor, pois você pode escrever um Window Listener para fazer algumas operações de limpeza antes de realmente deixar o aplicativo.

Esse listener de janela permite que você defina:

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

Experimentar:

System.exit(0);

Rude, mas eficaz.

Daniel Spiewak
fonte
Infelizmente, isso é muito bruto para mim. Quero que os eventos de fechamento da janela sejam processados ​​para algumas ações de limpeza. OK, eu poderia fazer um System.exit com um SwingUtils.invokeLater, mas prefiro fazer a coisa certa.
Hans-Peter Störr
5
System.exit (0) não é apenas bruto, é maligno. Verifique todos os quadros, chame o descarte. Se isso falhar, execute um depurador e veja quais threads não-daemon ainda estão ativos.
James Schek
Existem muitos bugs, mesmo dentro do JDK, que deixam o processo ativo. Então, +1 para exit (), mas você pode querer desativá-lo em compilações de depuração.
Tom Hawtin - tackline
11

Pode ser que o caminho seguro seja 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
fonte
3
Estilo muito ruim de nomear uma variável Framecom uma letra maiúscula, exatamente como o nome de uma classe…
Jean-Philippe Pellet
Muito obrigado! Essa é uma das melhores maneiras!
rzaaeeff de
9

O programa a seguir inclui código que encerrará um programa sem threads estranhos sem chamar explicitamente System.exit (). Para aplicar este exemplo a aplicativos que usam threads / ouvintes / temporizadores / etc, basta inserir o código de limpeza solicitando (e, se aplicável, aguardando) seu encerramento antes de WindowEvent ser iniciado manualmente em actionPerformed ().

Para aqueles que desejam copiar / colar código capaz de funcionar exatamente como mostrado, um método principal um pouco feio, mas irrelevante, está incluído no final.

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

Se bem entendi, você deseja fechar o aplicativo mesmo que o usuário não tenha clicado no botão Fechar. Você precisará registrar WindowEvents talvez com addWindowListener () ou enableEvents (), o que for mais adequado às suas necessidades.

Você pode então invocar o evento com uma chamada para processWindowEvent (). Aqui está um código de amostra que criará um JFrame, aguarde 5 segundos e feche o JFrame sem interação do usuário.

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 você pode ver, o método processWindowEvent () faz com que o evento WindowClosed seja disparado onde você tem a oportunidade de fazer algum código de limpeza antes de fechar o aplicativo.

Vincent Ramdhanie
fonte
windowClosed deve chamar dispose () e não System.exit (0).
James Schek
2

Dê uma olhada na documentação da Oracle .

A partir do JDK 1.4, um aplicativo será encerrado se:

  • Não há componentes AWT ou Swing exibíveis.
  • Não há eventos nativos na fila de eventos nativos.
  • Não há eventos AWT em java EventQueues.

Cornercases:

O documento afirma que alguns pacotes criam componentes exibíveis sem liberá-los. Um programa que chama Toolkit.getDefaultToolkit () não termina. é, entre outros, dado como exemplo.

Além disso, outros processos podem manter o AWT ativo quando, por qualquer motivo, estão enviando eventos para a fila de eventos nativos.

Também notei que em alguns sistemas leva alguns segundos antes que o aplicativo realmente seja encerrado.

Reiner Lange
fonte
0

Eu acho que a ideia aqui é o WindowListener - você pode adicionar qualquer código que gostaria de executar antes de desligar

Tamas Rev
fonte
0

Em resposta a outros comentários, DISPOSE_ON_CLOSE não parece sair do aplicativo corretamente - ele apenas destrói a janela, mas o aplicativo continuará em execução. Se você deseja encerrar o aplicativo, use EXIT_ON_CLOSE.

JSideris
fonte
Essa resposta sugere exatamente o oposto da resposta de James Schek. Qual está correto? (quando eu testo com um aplicativo simples, ambos parecem funcionar ...)
OR Mapper
Não me lembro como testei isso agora.
JSideris