Cara menutup aplikasi Java Swing dari kode

94

Apa cara yang tepat untuk menghentikan aplikasi Swing dari kode, dan apa saja kekurangannya?

Saya mencoba menutup aplikasi saya secara otomatis setelah pengatur waktu menyala. Tapi hanya menelepon dispose()pada JFrametidak melakukan trik - jendela lenyap tetapi aplikasi tidak mengakhiri. Namun saat menutup jendela dengan tombol tutup, aplikasi akan berhenti. Apa yang harus saya lakukan?

hstoerr
sumber
Silakan posting potongan kode timer Anda.
James Schek

Jawaban:

106

Tindakan penutupan default JFrame Anda dapat disetel ke " DISPOSE_ON_CLOSE" daripada EXIT_ON_CLOSE(mengapa orang tetap menggunakan EXIT_ON_CLOSE berada di luar jangkauan saya).

Jika Anda memiliki jendela yang tidak dibuang atau utas non-daemon, aplikasi Anda tidak akan dihentikan. Ini harus dianggap sebagai kesalahan (dan menyelesaikannya dengan System.exit adalah ide yang sangat buruk).

Penyebab paling umum adalah java.util.Timer dan Thread khusus yang Anda buat. Keduanya harus disetel ke daemon atau harus dimatikan secara eksplisit.

Jika Anda ingin memeriksa semua frame yang aktif, Anda dapat menggunakan Frame.getFrames(). Jika semua Windows / Frames dibuang, gunakan debugger untuk memeriksa utas non-daemon yang masih berjalan.

James Schek
sumber
Dalam kasus saya, saya menemukan dengan debugger bahwa saya memiliki Swingworker yang masih aktif. Saya menyebut metode batal (true) di WindowClose Eventlistener dan program berakhir sekarang. Terima kasih!
Hans-Peter Störr
Perhatikan bahwa Anda mungkin harus memanggil buang pada setiap bingkai, dan biasanya itu "cukup" (meskipun menyetel tindakan tutup default ke EXIT_ON_CLOSE mungkin juga bukan ide yang buruk).
rogerdpack
Bagaimana jika Anda memiliki satu jendela membuka yang lain, tetapi tidak membuangnya sendiri, sehingga Anda dapat menggunakannya untuk backjendela? Jika jendela kedua kemudian ditutup dan Anda menggunakan DISPOSE_ON_CLOSEprogram tidak berhenti karena jendela pertama masih "tidak dibuang" ... apakah ada cara untuk menyelesaikannya tanpa menggunakan DISPOSE_ON_CLOSE?
Svend Hansen
Jendela pertama harus menambahkan dirinya sendiri sebagai pendengar untuk jendela kedua dan mengambil tindakan yang sesuai.
James Schek
3
Menggunakan EXIT_ON_CLOSE akan menghentikan aplikasi secara paksa. Jika Anda perlu mengambil tindakan sebelum program keluar (seperti menyimpan data kerja), maka Anda harus memanfaatkan penangan kejadian JVM alih-alih hanya menggunakan penangan kejadian ayunan biasa. Keduanya akan "berfungsi", tetapi EXIT_ON_CLOSE akan menyebabkan masalah pada aplikasi yang lebih kompleks.
James Schek
34

Saya kira EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

sebelumnya System.exit(0)lebih baik karena Anda dapat menulis Pendengar Jendela untuk melakukan beberapa operasi pembersihan sebelum benar-benar meninggalkan aplikasi.

Pemroses jendela itu memungkinkan Anda untuk menentukan:

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

Mencoba:

System.exit(0);

Kasar, tapi efektif.

Daniel Spiewak
sumber
Sayangnya ini terlalu kasar untuk saya. Saya ingin acara penutupan jendela diproses untuk beberapa tindakan pembersihan. Oke, saya bisa melakukan System.exit dengan SwingUtils.invokeLater, tapi saya lebih suka melakukan hal yang benar.
Hans-Peter Störr
5
System.exit (0) tidak hanya kasar, tapi juga jahat. Periksa semua frame, panggil buang. Jika gagal, jalankan debugger dan lihat utas non-daemon apa yang masih hidup.
James Schek
Ada banyak bug bahkan di dalam JDK yang meninggalkan proses tersebut. Jadi +1 untuk exit (), tetapi Anda mungkin ingin menonaktifkannya dalam pembuatan debug.
Tom Hawtin - tackline
11

Mungkin cara yang aman adalah seperti:

    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
sumber
3
Gaya yang sangat buruk untuk menamai variabel Framedengan huruf kapital, persis seperti nama kelas…
Jean-Philippe Pellet
Terima kasih banyak! Ini adalah salah satu cara terbaik!
rzaaeeff
9

Program berikut menyertakan kode yang akan menghentikan program yang kekurangan utas asing tanpa secara eksplisit memanggil System.exit (). Untuk menerapkan contoh ini ke aplikasi yang menggunakan utas / pendengar / pengatur waktu / dll, seseorang hanya perlu menyisipkan kode pembersihan yang meminta (dan, jika ada, menunggu) penghentiannya sebelum WindowEvent dimulai secara manual dalam actionPerformed ().

Bagi mereka yang ingin menyalin / menempel kode yang mampu berjalan persis seperti yang ditunjukkan, metode utama yang sedikit jelek tetapi tidak relevan disertakan di bagian akhir.

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

Jika saya memahami Anda secara benar, Anda ingin menutup aplikasi meskipun pengguna tidak mengklik tombol tutup. Anda perlu mendaftarkan WindowEvents mungkin dengan addWindowListener () atau enableEvents () mana saja yang lebih sesuai dengan kebutuhan Anda.

Anda kemudian dapat menjalankan acara dengan panggilan ke processWindowEvent (). Berikut adalah contoh kode yang akan membuat JFrame, tunggu 5 detik dan tutup JFrame tanpa interaksi pengguna.

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

}

Seperti yang Anda lihat, metode processWindowEvent () menyebabkan acara WindowClosed dijalankan di mana Anda memiliki kesempatan untuk melakukan beberapa kode pembersihan jika Anda memerlukan sebelum menutup aplikasi.

Vincent Ramdhanie
sumber
windowClosed harus memanggil dispose () bukan System.exit (0).
James Schek
2

Lihat Dokumentasi Oracle .

Mulai dari JDK 1.4, Aplikasi berakhir jika:

  • Tidak ada komponen AWT atau Swing yang dapat ditampilkan.
  • Tidak ada acara asli di antrean acara asli.
  • Tidak ada acara AWT di java EventQueues.

Cornercases:

Dokumen tersebut menyatakan bahwa beberapa paket membuat komponen yang dapat ditampilkan tanpa melepaskannya. Program yang memanggil Toolkit.getDefaultToolkit () tidak akan berhenti. antara lain diberikan sebagai contoh.

Juga Proses lain dapat membuat AWT tetap hidup ketika mereka, untuk alasan apa pun, mengirim peristiwa ke antrean peristiwa asli.

Juga saya perhatikan bahwa pada beberapa Sistem dibutuhkan beberapa detik sebelum Aplikasi benar-benar berhenti.

Reiner Lange
sumber
0

Saya pikir, idenya ada di sini WindowListener - Anda dapat menambahkan kode apa pun di sana yang ingin Anda jalankan sebelum semuanya dimatikan

Tamas Rev
sumber
0

Menanggapi komentar lain, DISPOSE_ON_CLOSE tampaknya tidak keluar dari aplikasi dengan benar - ini hanya merusak jendela, tetapi aplikasi akan terus berjalan. Jika Anda ingin menghentikan aplikasi, gunakan EXIT_ON_CLOSE.

JSideris
sumber
Tanggapan ini menunjukkan kebalikan dari jawaban James Schek. Yang mana yang benar? (ketika saya menguji dengan aplikasi sederhana, keduanya tampaknya berfungsi ...)
ATAU Mapper
Tidak ingat bagaimana saya mengujinya sekarang.
JSideris