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?


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.


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

16

Experimentar:

System.exit(0);

Rude, mas eficaz.


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

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

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.


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.


0

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


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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.