코드에서 Java Swing 애플리케이션을 닫는 방법


94

코드에서 Swing 애플리케이션을 종료하는 적절한 방법은 무엇이며 함정은 무엇입니까?

타이머가 실행 된 후 자동으로 애플리케이션을 닫으려고했습니다. 그러나를 호출 dispose()하는 것만으로 JFrame는 트릭 이 발생 하지 않았습니다. 창이 사라졌지 만 응용 프로그램이 종료되지 않았습니다. 그러나 닫기 버튼으로 창을 닫으면 응용 프로그램이 종료됩니다. 어떻게해야합니까?


타이머 코드 스 니펫을 게시하세요.
James Schek

답변:


106

JFrame 기본 닫기 작업을 DISPOSE_ON_CLOSE대신 " " 로 설정할 수 있습니다 EXIT_ON_CLOSE(사람들이 EXIT_ON_CLOSE를 계속 사용하는 이유는 저 밖에 없습니다).

처리되지 않은 창이나 데몬이 아닌 스레드가있는 경우 응용 프로그램이 종료되지 않습니다. 이것은 오류로 간주되어야합니다 (System.exit로 해결하는 것은 매우 나쁜 생각입니다).

가장 일반적인 원인은 java.util.Timer와 사용자가 만든 사용자 지정 스레드입니다. 둘 다 daemon으로 설정하거나 명시 적으로 종료해야합니다.

모든 활성 프레임을 확인하려면을 사용할 수 있습니다 Frame.getFrames(). 모든 Windows / 프레임이 삭제 된 경우 디버거를 사용하여 아직 실행중인 비 데몬 스레드를 확인합니다.


제 경우에는 디버거에서 Swingworker가 여전히 활성화되어 있음을 발견했습니다. WindowClose Eventlistener에서 cancel (true) 메서드를 호출하면 프로그램이 종료됩니다. 감사!
Hans-Peter Störr

각 프레임에 대해 dispose를 호출해야 할 수 있으며 일반적으로 "충분합니다"(기본 닫기 작업을 EXIT_ON_CLOSE로 설정하는 것도 나쁘지 않을 수 있음).
rogerdpack 2010

한 창에서 다른 창을 열었지만 자체적으로 배치하지 않아 back창에 사용할 수 있다면 어떨까요? 두 번째 창이 닫히고 DISPOSE_ON_CLOSE첫 번째 창이 여전히 "분할되지 않은"상태이기 때문에 프로그램이 종료되지 않는 경우 ... 사용 하지 않고 해결할 수있는 방법이 DISPOSE_ON_CLOSE있습니까?
Svend Hansen

첫 번째 창은 두 번째 창에 대한 리스너로 자신을 추가하고 적절한 조치를 취해야합니다.
James Schek

3
EXIT_ON_CLOSE를 사용하면 응용 프로그램이 강제로 종료됩니다. 프로그램이 종료되기 전에 조치를 취해야하는 경우 (예 : 작업 데이터 저장) 일반 스윙 이벤트 핸들러를 사용하는 대신 JVM 이벤트 핸들러를 활용해야합니다. 둘 다 "작동"하지만 EXIT_ON_CLOSE는 더 복잡한 앱에 문제를 일으킬 것입니다.
James Schek 2013 년

34

EXIT_ON_CLOSE 인 것 같아요

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

실제로 앱을 떠나기 전에 몇 가지 정리 작업을 수행 System.exit(0)하는 Window Listener 를 작성할 수 있기 때문에 before 가 더 좋습니다 .

이 창 리스너를 사용하면 다음을 정의 할 수 있습니다.

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

시험:

System.exit(0);

조잡하지만 효과적입니다.


불행히도 이것은 나에게 너무 조잡합니다. 일부 정리 작업을 위해 창 닫기 이벤트를 처리하고 싶습니다. 좋습니다. SwingUtils.invokeLater를 사용하여 System.exit를 수행 할 수 있지만 적절한 작업을 수행하고 싶습니다.
Hans-Peter Störr

5
System.exit (0)은 단지 조잡한 것이 아니라 사악합니다. 모든 프레임을 확인하고 dispose를 호출하십시오. 실패하면 디버거를 실행하고 데몬이 아닌 스레드가 아직 살아 있는지 확인하십시오.
James Schek

프로세스를 떠나는 JDK 내에서도 많은 버그가 있습니다. 따라서 exit ()에 +1하지만 디버그 빌드에서 비활성화 할 수 있습니다.
Tom Hawtin-tackline

11

안전한 방법은 다음과 같습니다.

    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
아주 나쁜 스타일은 변수 이름을 지정합니다 Frame정확히 ... 클래스의 이름처럼 대문자로를
장 - 필립 펠렛

정말 고맙습니다! 이것은 가장 좋은 방법 중 하나입니다!
rzaaeeff

9

다음 프로그램에는 System.exit ()를 명시 적으로 호출하지 않고 외부 스레드가없는 프로그램을 종료하는 코드가 포함되어 있습니다. 이 예제를 스레드 / 수신기 / 타이머 등을 사용하는 응용 프로그램에 적용하려면 WindowEvent가 actionPerformed () 내에서 수동으로 시작되기 전에 종료를 요청 (해당되는 경우 대기)하는 정리 코드 만 삽입하면됩니다.

표시된대로 정확하게 실행할 수있는 코드를 복사 / 붙여 넣기하려는 사람들을 위해 약간 못 생겼지 만 관련이없는 주요 방법이 끝에 포함됩니다.

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

사용자가 닫기 버튼을 클릭하지 않았더라도 응용 프로그램을 닫고 싶습니다. 필요에 더 적합한 addWindowListener () 또는 enableEvents ()를 사용하여 WindowEvents를 등록해야합니다.

그런 다음 processWindowEvent ()를 호출하여 이벤트를 호출 할 수 있습니다. 다음은 JFrame을 만들고 5 초간 기다린 후 사용자 상호 작용없이 JFrame을 닫는 샘플 코드입니다.

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

}

보시다시피 processWindowEvent () 메서드는 응용 프로그램을 닫기 전에 필요한 경우 코드를 정리할 기회가있는 곳에서 WindowClosed 이벤트를 발생시킵니다.


windowClosed는 System.exit (0)이 아닌 dispose ()를 호출해야합니다.
James Schek

2

Oracle 문서를 살펴보십시오 .

JDK 1.4부터 다음과 같은 경우 응용 프로그램이 종료됩니다.

  • 표시 가능한 AWT 또는 Swing 구성 요소가 없습니다.
  • 기본 이벤트 큐에는 기본 이벤트가 없습니다.
  • Java EventQueues에는 AWT 이벤트가 없습니다.

코너 케이스 :

문서에는 일부 패키지가 릴리스하지 않고 표시 가능한 구성 요소를 생성한다고 명시되어 있습니다. Toolkit.getDefaultToolkit ()을 호출하는 프로그램은 종료되지 않습니다. 예를 들면 다른 것들 중입니다.

또한 다른 프로세스는 어떤 이유로 든 이벤트를 네이티브 이벤트 큐로 보낼 때 AWT를 활성 상태로 유지할 수 있습니다.

또한 일부 시스템에서는 응용 프로그램이 실제로 종료되기까지 몇 초가 걸립니다.


0

아이디어는 여기에 WindowListener라고 생각합니다. 작업이 종료되기 전에 실행하려는 코드를 추가 할 수 있습니다.


0

다른 의견에 대한 응답으로 DISPOSE_ON_CLOSE는 응용 프로그램을 제대로 종료하지 않는 것 같습니다. 창만 파괴 할뿐 응용 프로그램은 계속 실행됩니다. 애플리케이션을 종료하려면 EXIT_ON_CLOSE를 사용하십시오.


이 응답은 James Schek의 대답과 정반대임을 시사합니다. 어느 것이 맞습니까? (간단한 응용 프로그램으로 테스트하면 둘 다 작동하는 것 같습니다 ...)
또는 Mapper

지금 어떻게 테스트했는지 기억 나지 않습니다.
JSideris
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.