【问题标题】:How to close a Java Swing application from the code如何从代码中关闭 Java Swing 应用程序
【发布时间】:2010-09-20 11:05:13
【问题描述】:

从代码中终止 Swing 应用程序的正确方法是什么,有哪些陷阱?

我试图在计时器触发后自动关闭我的应用程序。但是仅仅在JFrame 上调用dispose() 并没有成功——窗口消失了,但应用程序没有终止。但是,当使用关闭按钮关闭窗口时,应用程序会终止。我该怎么办?

【问题讨论】:

  • 请张贴您的计时器的代码 sn-p。

标签: java swing


【解决方案1】:

您的 JFrame 默认关闭操作可以设置为“DISPOSE_ON_CLOSE”而不是 EXIT_ON_CLOSE(为什么人们一直使用 EXIT_ON_CLOSE 超出了我的理解)。

如果您有任何未处理的窗口或非守护线程,您的应用程序将不会终止。这应该被认为是一个错误(使用 System.exit 解决它是一个非常糟糕的主意)。

最常见的罪魁祸首是 java.util.Timer 和您创建的自定义线程。两者都应该设置为 daemon 或者必须被显式杀死。

如果要检查所有活动帧,可以使用Frame.getFrames()。如果所有 Windows/Frames 都已处理完毕,则使用调试器检查是否有任何仍在运行的非守护线程。

【讨论】:

  • 就我而言,我使用调试器发现我的 Swingworker 仍然处于活动状态。我在 WindowClose Eventlistener 中调用了它的 cancel(true) 方法,程序现在终止。谢谢!
  • 请注意,您可能必须在每一帧上调用 dispose,通常这是“足够的”(尽管将默认关闭操作设置为 EXIT_ON_CLOSE 也可能不是一个坏主意)。
  • 如果您有一个窗口打开另一个窗口,但没有自行处理,以便您可以将其用于back 窗口,该怎么办?如果第二个窗口随后关闭并且您使用DISPOSE_ON_CLOSE,则程序不会终止,因为第一个窗口仍然“未释放”......有没有办法使用DISPOSE_ON_CLOSE解决这个问题without ?
  • 第一个窗口应将自己添加为第二个窗口的侦听器并采取适当的措施。
  • 使用 EXIT_ON_CLOSE 将强制终止应用程序。如果您需要在程序退出之前采取行动(例如保存工作数据),那么您必须使用 JVM 事件处理程序,而不仅仅是使用普通的 swing 事件处理程序。两者都会“工作”,但 EXIT_ON_CLOSE 会导致更复杂的应用出现问题。
【解决方案2】:

我猜是EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

System.exit(0) 之前更好,因为您可以在实际离开应用程序之前编写Window Listener 来进行一些清理操作。

那个窗口监听器允许你定义:

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

【讨论】:

    【解决方案3】:

    试试:

    System.exit(0);
    

    粗鲁,但有效。

    【讨论】:

    • 不幸的是,这对我来说太粗糙了。我希望为某些清理操作处理窗口关闭事件。好的,我可以使用 SwingUtils.invokeLater 执行 System.exit,但我宁愿做正确的事情。
    • System.exit(0) 不仅粗鲁,而且很邪恶。检查所有帧,调用 dispose。如果失败,请运行调试器并查看哪些非守护线程仍处于活动状态。
    • 即使在 JDK 中也存在大量的 bug,这些 bug 会导致该过程。所以 +1 退出(),但你可能想在调试版本中禁用它。
    【解决方案4】:

    可能是安全的方式是这样的:

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

    【讨论】:

    • 用大写字母命名变量Frame 的风格非常糟糕,就像一个类的名称......
    • 非常感谢!这是最好的方法之一!
    【解决方案5】:

    以下程序包含的代码将在不显式调用 System.exit() 的情况下终止缺少无关线程的程序。为了将此示例应用于使用线程/侦听器/计时器/等的应用程序,只需在 actionPerformed() 中手动启动 WindowEvent 之前插入清理代码以请求(并且,如果适用,等待)它们的终止。

    对于那些希望复制/粘贴能够完全按照所示运行的代码的人,最后包含了一个有点难看但在其他方面无关紧要的 main 方法。

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

    【讨论】:

      【解决方案6】:

      如果我理解正确,即使用户没有单击关闭按钮,您也想关闭应用程序。您可能需要使用更适合您需要的 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 应该调用 dispose() 而不是 System.exit(0)。
      【解决方案7】:

      看看Oracle Documentation

      从 JDK 1.4 开始,应用程序在以下情况下终止:

      • 没有可显示的 AWT 或 Swing 组件。
      • 本机事件队列中没有本机事件。
      • java EventQueues 中没有 AWT 事件。

      转角:

      该文档指出,某些包创建可显示组件而不释放它们。A program which calls Toolkit.getDefaultToolkit() won't terminate. 是其中一个示例。

      当其他进程出于某种原因将事件发送到本机事件队列时,它们也可以保持 AWT 处于活动状态。

      我还注意到,在某些系统上,应用程序实际终止需要几秒钟的时间。

      【讨论】:

        【解决方案8】:

        我认为,这里的想法是 WindowListener - 您可以在其中添加任何您想在关闭之前运行的代码

        【讨论】:

          【解决方案9】:

          响应其他 cmets,DISPOSE_ON_CLOSE 似乎没有正确退出应用程序——它只是破坏了窗口,但应用程序会继续运行。如果您想终止应用程序,请使用 EXIT_ON_CLOSE。

          【讨论】:

          • 此响应与 James Schek 的回答完全相反。哪一个是正确的? (当我使用一个简单的应用程序进行测试时,两者似乎都可以工作......)
          • 不记得我现在是如何测试的了。
          猜你喜欢
          • 1970-01-01
          • 2020-07-19
          • 2017-03-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-19
          • 2012-09-13
          相关资源
          最近更新 更多