2008-11-03 5 views
84

Jaki jest właściwy sposób zakończenia aplikacji Swing z kodu i jakie są pułapki?Jak zamknąć aplikację Java Swing z kodu

Próbowałem zamknąć aplikację automatycznie po uruchomieniu licznika czasu. Ale po prostu wywołanie dispose() na JFrame nie załatwiło sprawy - okno zniknęło, ale aplikacja się nie zakończyła. Jednak podczas zamykania okna za pomocą przycisku zamykania aplikacja kończy działanie. Co powinienem zrobić?

+0

Proszę opublikować fragment kodu swojego licznika czasu. –

Odpowiedz

99

Domyślne działanie zamykające JFrame można ustawić na "DISPOSE_ON_CLOSE" zamiast EXIT_ON_CLOSE (dlaczego ludzie nadal używają EXIT_ON_CLOSE jest poza mną).

Jeśli masz nieukierunkowane okna lub wątki inne niż demon, aplikacja nie zostanie zakończona. Powinno to zostać uznane za błąd (i rozwiązanie go z System.exit jest bardzo złym pomysłem).

Najczęstszymi sprawcami są java.util.Timer i niestandardowy wątek, który utworzyłeś. Oba powinny być ustawione na demona lub muszą być jawnie zabite.

Jeśli chcesz sprawdzić wszystkie aktywne ramki, możesz użyć Frame.getFrames(). Jeśli wszystkie okna/ramki są usuwane, użyj debuggera, aby sprawdzić wszystkie wątki nie będące demonami, które wciąż są uruchomione.

+0

W moim przypadku odkryłem z debuggerem, że miałem Swingworker nadal aktywny. Wywołałem jego metodę cancel (true) w Eventlistener WindowClose, a program został zakończony. Dzięki! –

+0

Zauważ, że możesz potrzebować wywołać dispose na każdej ramce, i zazwyczaj jest to "wystarczająca" (chociaż ustawienie domyślnej akcji zamykającej na EXIT_ON_CLOSE również nie jest złym pomysłem). – rogerdpack

+0

Co zrobić, jeśli masz jedno okno otwierające drugie, ale nie wyrzucające się, aby można było użyć go do okna 'z powrotem'? Jeśli drugie okno jest wtedy zamknięte i używasz 'DISPOSE_ON_CLOSE' program się nie kończy, ponieważ pierwsze okno jest nadal" nieusuwalne "... czy istnieje sposób rozwiązania tego bez użycia' DISPOSE_ON_CLOSE'? –

12

Spróbuj:

System.exit(0); 

surowe, ale skuteczne.

+0

Niestety jest to dla mnie zbyt prymitywne. Chcę, aby zdarzenia zamknięcia okna były przetwarzane dla niektórych działań czyszczących. OK, mógłbym zrobić System.exit z SwingUtils.invokeLater, ale wolałbym zrobić to, co należy. –

+4

System.exit (0) jest nie tylko prymitywny, ale zły. Sprawdź wszystkie ramki, zadzwoń do dyspozycji. Jeśli to się nie powiedzie, uruchom debuger i zobacz, jakie wątki nie będące demonami jeszcze istnieją. –

+0

W JDK było mnóstwo błędów, które opuściły proces. Więc +1 do exit(), ale możesz chcieć wyłączyć to w kompilacjach debugowania. –

33

I odgadnąć EXIT_ON_CLOSE

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

przed System.exit(0) jest lepiej, ponieważ można napisać Window Listener dokonać pewnych operacji czyszczenia, zanim rzeczywiście opuszczania aplikacji.

To okno słuchacz pozwala na zdefiniowane:

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

Jeśli rozumiem zostanie poprawnie chcesz zamknąć aplikację, nawet jeśli użytkownik nie kliknij przycisk Zamknij. Będziesz musiał zarejestrować WindowEvents przy pomocy addWindowListener() lub enableEvents(), które lepiej pasują do twoich potrzeb.

Następnie można wywołać zdarzenie wywołując processWindowEvent(). Oto przykładowy kod, który utworzy JFrame, poczekaj 5 sekund i zamknij JFrame bez interakcji użytkownika.

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

} 

Jak widać, metoda processWindowEvent() wywołuje zdarzenie WindowClosed do opalania, gdzie masz przegapisz zrobić kilka posprzątać kod jeśli wymagają przed zamknięciem aplikacji.

+0

windowClosed powinien wywołać dispose() nie System.exit (0). –

0

myślę, że pomysł jest tu WindowListener - można dodać dowolny kod tam, że chcesz uruchomić zanim sprawa zamyka

9

Poniższy program zawiera kod, który zakończy program brakuje obcych wątki bez jawne wywoływanie System.exit(). Aby zastosować ten przykład do aplikacji używających wątków/detektorów/liczników czasu/etc, wystarczy wstawić kod czyszczenia żądając (i jeśli ma to zastosowanie, oczekując) ich zakończenia przed ręcznym uruchomieniem WindowEvent w actionPerformed().

Dla tych, którzy chcą skopiować/wkleić kod, który może działać dokładnie tak, jak pokazano, na końcu dołączona jest nieco brzydka, ale poza tym nieistotna metoda główna.

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

Może być bezpieczny sposób jest coś takiego:

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

Bardzo zły styl na nazwę zmiennej 'Frame' z wielką literą, dokładnie tak jak nazwa klasy ... –

+0

Dziękuję bardzo! To jeden z najlepszych sposobów! – rzaaeeff

0

W odpowiedzi na inne komentarze, DISPOSE_ON_CLOSE nie wydaje się właściwie zamknąć aplikację - to tylko niszczy okno, ale aplikacja kontynuuj bieg. Jeśli chcesz zakończyć aplikację, użyj EXIT_ON_CLOSE.

+0

Ta odpowiedź sugeruje dokładne przeciwieństwo odpowiedzi Jamesa Scheka. Który jest prawidłowy? (kiedy testuję za pomocą prostej aplikacji, oba wydają się działać ...) –

+0

Nie pamiętam, jak to teraz przetestowałem. – JSideris

2

Spójrz na Oracle Documentation.

Począwszy od JDK 1.4 aplikacja kończy się wtedy, gdy:

  • Brak wyświetlanych AWT lub Swing składników.
  • Brak rodzimych zdarzeń w rodzimej kolejce zdarzeń.
  • Brak zdarzeń AWT w zdarzeniach EventQueues Java.

Cornercases:

Dokument stwierdza, że ​​niektóre pakiety tworzyć wyświetlanych elementów bez ich zwolnieniu. A program which calls Toolkit.getDefaultToolkit() won't terminate. jest podany jako przykład.

Również inne procesy mogą utrzymywać AWT przy życiu, kiedy, z jakiegokolwiek powodu, wysyłają zdarzenia do rodzimej kolejki zdarzeń.

Zauważyłem także, że w niektórych systemach trwa kilka sekund zanim aplikacja się zakończy.