Cách đóng ứng dụng Java Swing từ mã


94

Cách thích hợp để chấm dứt ứng dụng Swing khỏi mã là gì, và những cạm bẫy là gì?

Tôi đã cố gắng tự động đóng ứng dụng của mình sau khi bộ hẹn giờ kích hoạt. Nhưng chỉ cần gọi dispose()vào JFramekhông làm được thủ thuật - cửa sổ biến mất nhưng ứng dụng không kết thúc. Tuy nhiên, khi đóng cửa sổ bằng nút đóng, ứng dụng sẽ kết thúc. Tôi nên làm gì?


Vui lòng đăng một đoạn mã về bộ đếm thời gian của bạn.
James Schek

Câu trả lời:


106

Hành động đóng mặc định trên JFrame của bạn có thể được đặt thành " DISPOSE_ON_CLOSE" thay vì EXIT_ON_CLOSE(tại sao mọi người tiếp tục sử dụng EXIT_ON_CLOSE ngoài tôi).

Nếu bạn có bất kỳ cửa sổ không thể tranh cãi hoặc chuỗi không phải daemon, ứng dụng của bạn sẽ không kết thúc. Đây nên được coi là một lỗi (và giải quyết nó bằng System.exit là một ý tưởng rất tồi).

Thủ phạm phổ biến nhất là java.util.Timer và một Chủ đề tùy chỉnh mà bạn đã tạo. Cả hai phải được đặt thành daemon hoặc phải bị giết một cách rõ ràng.

Nếu bạn muốn kiểm tra tất cả các khung hoạt động, bạn có thể sử dụng Frame.getFrames(). Nếu tất cả Windows / Frames bị loại bỏ, thì hãy sử dụng trình gỡ lỗi để kiểm tra xem có bất kỳ luồng không phải daemon nào vẫn đang chạy hay không.


Trong trường hợp của tôi, tôi đã phát hiện ra với trình gỡ lỗi rằng tôi có một Swingworker vẫn đang hoạt động. Tôi đã gọi phương thức hủy (true) của nó trong WindowClose Eventlistener và chương trình kết thúc ngay bây giờ. Cảm ơn!
Hans-Peter Störr

Lưu ý rằng bạn có thể phải gọi vứt bỏ trên mỗi khung và thường là "đủ" (mặc dù đặt hành động đóng mặc định thành EXIT_ON_CLOSE có lẽ cũng không phải là một ý kiến ​​tồi).
rogerdpack

Điều gì sẽ xảy ra nếu bạn có một cửa sổ đang mở một cửa sổ khác, nhưng không tự hủy, để bạn có thể sử dụng nó cho một backcửa sổ? Nếu cửa sổ thứ hai sau đó bị đóng và bạn sử dụng DISPOSE_ON_CLOSEchương trình không kết thúc vì cửa sổ đầu tiên vẫn còn "không thể tranh cãi" ... có cách nào để giải quyết điều đó mà không cần sử dụng DISPOSE_ON_CLOSEkhông?
Svend Hansen

Cửa sổ đầu tiên phải tự thêm vào làm trình nghe cho cửa sổ thứ hai và thực hiện hành động thích hợp.
James Schek,

3
Việc sử dụng EXIT_ON_CLOSE sẽ buộc chấm dứt ứng dụng. Nếu bạn cần thực hiện hành động trước khi chương trình thoát (chẳng hạn như lưu dữ liệu đang làm việc), thì bạn phải nhấn vào trình xử lý sự kiện JVM thay vì chỉ sử dụng trình xử lý sự kiện swing bình thường. Cả hai đều sẽ "hoạt động", nhưng EXIT_ON_CLOSE sẽ gây ra sự cố cho các ứng dụng phức tạp hơn.
James Schek

34

Tôi đoán là EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

trước System.exit(0)thì tốt hơn vì bạn có thể viết Trình nghe cửa sổ để thực hiện một số thao tác dọn dẹp trước khi thực sự rời khỏi ứng dụng.

Trình nghe cửa sổ đó cho phép bạn xác định:

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

Thử:

System.exit(0);

Thô nhưng hiệu quả.


Thật không may, điều này là quá thô đối với tôi. Tôi muốn các sự kiện đóng cửa sổ được xử lý cho một số hành động dọn dẹp. OK, tôi có thể thực hiện System.exit với SwingUtils.invokeLater, nhưng tôi muốn làm điều thích hợp hơn.
Hans-Peter Störr

5
System.exit (0) không chỉ thô thiển mà còn xấu xa. Kiểm tra tất cả các khung, xử lý cuộc gọi. Nếu không thành công, hãy chạy trình gỡ lỗi và xem những luồng không phải daemon nào vẫn còn sống.
James Schek

Có rất nhiều lỗi ngay cả trong JDK khiến quá trình này bị ảnh hưởng. Vì vậy, +1 để thoát (), nhưng bạn có thể muốn tắt nó trong các bản dựng gỡ lỗi.
Tom Hawtin - tackline

11

Có thể là cách an toàn là một cái gì đó như:

    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
Phong cách khá xấu đến tên một biến Framevới một chữ cái viết hoa, giống hệt như tên của một lớp học ...
Jean-Philippe Pellet

Cảm ơn bạn rất nhiều! Đây là một trong những cách tốt nhất!
rzaaeeff

9

Chương trình sau bao gồm mã sẽ chấm dứt một chương trình thiếu các luồng không liên quan mà không gọi System.exit () một cách rõ ràng. Để áp dụng ví dụ này cho các ứng dụng sử dụng luồng / người nghe / bộ định thời / v.v., người ta chỉ cần chèn mã dọn dẹp yêu cầu (và, nếu có, đang chờ) kết thúc trước khi WindowEvent được khởi tạo theo cách thủ công trong actionPerformed ().

Đối với những người muốn sao chép / dán mã có khả năng chạy chính xác như được hiển thị, một phương pháp chính hơi xấu xí nhưng không liên quan sẽ được bao gồm ở cuối.

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

Nếu tôi hiểu đúng về bạn, bạn muốn đóng ứng dụng ngay cả khi người dùng không nhấp vào nút đóng. Bạn sẽ cần đăng ký WindowEvents có thể bằng addWindowListener () hoặc enableEvents (), tùy theo nhu cầu của bạn.

Sau đó, bạn có thể gọi sự kiện bằng lời gọi processWindowEvent (). Đây là mã mẫu sẽ tạo JFrame, đợi 5 giây và đóng JFrame mà không cần người dùng tương tác.

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

}

Như bạn có thể thấy, phương thức processWindowEvent () khiến sự kiện WindowClosed được kích hoạt khi bạn có cơ hội thực hiện một số mã dọn dẹp nếu bạn yêu cầu trước khi đóng ứng dụng.


windowClosed nên gọi dispose () chứ không phải System.exit (0).
James Schek

2

Hãy xem Tài liệu Oracle .

Bắt đầu từ JDK 1.4, một Ứng dụng sẽ kết thúc nếu:

  • Không có thành phần AWT hoặc Swing có thể hiển thị.
  • Không có sự kiện gốc nào trong hàng đợi sự kiện gốc.
  • Không có sự kiện AWT nào trong java EventQueues.

Các góc:

Tài liệu nói rằng một số gói tạo ra các thành phần có thể hiển thị mà không cần giải phóng chúng. Một chương trình gọi Toolkit.getDefaultToolkit () sẽ không kết thúc. là một trong số những người khác được đưa ra làm ví dụ.

Ngoài ra, các Quy trình khác có thể giữ cho AWT tồn tại khi chúng, vì lý do gì đó, đang gửi các sự kiện vào hàng đợi sự kiện gốc.

Ngoài ra, tôi nhận thấy rằng trên một số Hệ thống, phải mất vài giây trước khi Ứng dụng thực sự kết thúc.


0

Tôi nghĩ, ý tưởng là ở đây WindowListener - bạn có thể thêm bất kỳ mã nào vào đó mà bạn muốn chạy trước khi mọi thứ tắt


0

Đáp lại các nhận xét khác, DISPOSE_ON_CLOSE dường như không thoát ứng dụng đúng cách - nó chỉ phá hủy cửa sổ, nhưng ứng dụng sẽ tiếp tục chạy. Nếu bạn muốn chấm dứt ứng dụng, hãy sử dụng EXIT_ON_CLOSE.


Câu trả lời này hoàn toàn ngược lại với câu trả lời của James Schek. Cái nào đúng? (khi tôi kiểm tra với một ứng dụng đơn giản, cả hai dường như đều hoạt động ...)
HOẶC Người lập bản đồ

Không thể nhớ làm thế nào tôi đã kiểm tra điều này bây giờ.
JSideris
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.