วิธีปิดแอปพลิเคชัน Java Swing จากโค้ด


94

วิธีที่เหมาะสมในการยุติแอปพลิเคชัน Swing จากโค้ดคืออะไรและข้อผิดพลาดคืออะไร?

ฉันพยายามปิดแอปพลิเคชันของฉันโดยอัตโนมัติหลังจากตัวจับเวลาเริ่มทำงาน เพียง แต่เรียกร้องdispose()บนJFrameไม่ได้ทำเคล็ดลับ - หน้าต่างหายไป แต่แอพลิเคชันไม่ยุติ อย่างไรก็ตามเมื่อปิดหน้าต่างด้วยปุ่มปิดแอปพลิเคชันจะยุติลง ฉันควรทำอย่างไรดี?


โปรดโพสต์ข้อมูลโค้ดของตัวจับเวลาของคุณ
James Schek

คำตอบ:


106

การดำเนินการปิดเริ่มต้น JFrame ของคุณสามารถตั้งค่าเป็น " DISPOSE_ON_CLOSE" แทนEXIT_ON_CLOSE(ทำไมผู้คนยังคงใช้ EXIT_ON_CLOSE อยู่นอกเหนือจากฉัน)

หากคุณมีหน้าต่างที่ไม่เปิดเผยหรือเธรดที่ไม่ใช่ daemon แอปพลิเคชันของคุณจะไม่ยุติ สิ่งนี้ควรถือเป็นข้อผิดพลาด (และการแก้ไขด้วย System.exit เป็นความคิดที่แย่มาก)

สาเหตุที่พบบ่อยที่สุดคือ java.util.Timer และเธรดแบบกำหนดเองที่คุณสร้างขึ้น ควรตั้งค่าทั้งสองเป็น daemon หรือต้องถูกฆ่าอย่างชัดเจน

Frame.getFrames()หากคุณต้องการที่จะตรวจสอบการใช้งานเฟรมที่ทุกท่านสามารถใช้ หาก Windows / Frames ทั้งหมดถูกกำจัดทิ้งให้ใช้ดีบักเกอร์เพื่อตรวจสอบเธรดที่ไม่ใช่ daemon ที่ยังทำงานอยู่


ในกรณีของฉันฉันค้นพบกับดีบักเกอร์ว่าฉันมี Swingworker ที่ยังทำงานอยู่ ฉันเรียกมันว่าวิธีการยกเลิก (true) ใน WindowClose Eventlistener และโปรแกรมจะยุติทันที ขอบคุณ!
Hans-Peter Störr

โปรดทราบว่าคุณอาจต้องเรียกการทิ้งในแต่ละเฟรมและโดยทั่วไปแล้วจะ "เพียงพอ" (แม้ว่าการตั้งค่าการดำเนินการปิดเริ่มต้นเป็น EXIT_ON_CLOSE ก็อาจไม่ใช่ความคิดที่เลวเช่นกัน)
rogerdpack

จะเกิดอะไรขึ้นถ้าคุณมีหน้าต่างหนึ่งเปิดขึ้นมาอีกหน้าต่างหนึ่ง แต่ไม่ทิ้งตัวมันเองเพื่อที่คุณจะได้ใช้เป็นbackหน้าต่างล่ะ? หากหน้าต่างที่สองปิดไปแล้วและคุณใช้DISPOSE_ON_CLOSEโปรแกรมไม่ได้ยุติลงเนื่องจากหน้าต่างแรกยัง "ไม่ถูกเปิดเผย" ... มีวิธีแก้ปัญหาโดยไม่ใช้DISPOSE_ON_CLOSEหรือไม่?
Svend Hansen

หน้าต่างแรกควรเพิ่มตัวเองเป็นผู้ฟังในหน้าต่างที่สองและดำเนินการตามความเหมาะสม
James Schek

3
การใช้ EXIT_ON_CLOSE จะบังคับให้แอปพลิเคชันยุติการใช้งาน หากคุณจำเป็นต้องดำเนินการก่อนที่โปรแกรมจะออก (เช่นการบันทึกข้อมูลการทำงาน) คุณต้องแตะเข้าไปในตัวจัดการเหตุการณ์ JVM แทนที่จะใช้เพียงแค่ตัวจัดการเหตุการณ์สวิงปกติ ทั้งสองจะ "ทำงาน" แต่ EXIT_ON_CLOSE จะทำให้เกิดปัญหากับแอปที่ซับซ้อนมากขึ้น
James Schek

34

ฉันเดาว่า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.");
}

16

ลอง:

System.exit(0);

หยาบคาย แต่มีประสิทธิภาพ


น่าเสียดายที่นี่มันหยาบคายเกินไปสำหรับฉัน ฉันต้องการให้มีการประมวลผลเหตุการณ์การปิดหน้าต่างสำหรับการดำเนินการล้างข้อมูลบางอย่าง ตกลงฉันสามารถทำ System.exit ด้วย SwingUtils.invokeLater ได้ แต่ฉันควรทำในสิ่งที่เหมาะสม
Hans-Peter Störr

5
System.exit (0) ไม่ใช่แค่ดิบ แต่มันชั่วร้าย ตรวจสอบเฟรมทั้งหมดโทรทิ้ง หากล้มเหลวให้รันดีบักเกอร์และดูว่าเธรดที่ไม่ใช่ daemon ยังมีชีวิตอยู่
James Schek

มีข้อบกพร่องมากมายแม้แต่ใน JDK ที่ออกจากกระบวนการ ดังนั้น +1 เพื่อออก () แต่คุณอาจต้องการปิดใช้งานในรุ่น debug
Tom Hawtin - แท็

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ด้วยตัวพิมพ์ใหญ่เหมือนกับชื่อของคลาส ...
Jean-Philippe Pellet

ขอบคุณมาก! นี่เป็นวิธีที่ดีที่สุดวิธีหนึ่ง!
rzaaeeff

9

โปรแกรมต่อไปนี้มีรหัสที่จะยุติโปรแกรมที่ไม่มีเธรดที่ไม่เกี่ยวข้องโดยไม่ต้องเรียก System.exit () อย่างชัดเจน ในการนำตัวอย่างนี้ไปใช้กับแอปพลิเคชันที่ใช้เธรด / listeners / timers / etc เราจำเป็นต้องใส่รหัสการล้างข้อมูลที่ร้องขอเท่านั้น (และหากมีให้รอ) การยกเลิกก่อนที่ 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

หากฉันเข้าใจคุณถูกต้องคุณต้องการปิดแอปพลิเคชันแม้ว่าผู้ใช้จะไม่ได้คลิกที่ปุ่มปิดก็ตาม คุณจะต้องลงทะเบียน WindowEvents ด้วย addWindowListener () หรือ enableEvents () แล้วแต่ความต้องการของคุณจะดีกว่า

จากนั้นคุณสามารถเรียกใช้เหตุการณ์ด้วยการเรียกใช้ 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)
James Schek

2

ลองดูที่เป็นเอกสารของ Oracle

เริ่มต้นจาก JDK 1.4 แอปพลิเคชันจะสิ้นสุดหาก:

  • ไม่มีส่วนประกอบ AWT หรือ Swing ที่แสดงได้
  • ไม่มีเหตุการณ์เนทีฟในคิวเหตุการณ์เนทีฟ
  • ไม่มีเหตุการณ์ AWT ใน java EventQueues

Cornercases:

เอกสารระบุว่าบางแพ็กเกจสร้างส่วนประกอบที่แสดงได้โดยไม่ต้องปล่อย โปรแกรมที่เรียก 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.