2011-12-09

Ultimate Swing, Teil 6

Wie Sie aus dem letzten Post wissen, ist es keine gute Idee, länger andauernde Aufgaben auf dem EDT auszuführen. Um Ihre Anwendung nicht zu blockieren, lagern Sie entsprechende Codeteile stattdessen in einen eigenen Thread aus. Wie so etwas aussehen kann, demonstriert Better. Hierbei handelt es sich praktisch um einen Klon von Bad. Nur die Implementierung der Methode actionPerformed() habe ich ausgetauscht.

      public void actionPerformed(ActionEvent e) {
        Thread t = new Thread(new Runnable() {
          @Override
          public void run() {
            for (int i = 1; i <= 10; i++) {
              try {
                Thread.sleep(1000);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
              System.out.println(i);
            }
            SwingUtilities.invokeLater(new Runnable() {
              @Override
              public void run() {
                button.setEnabled(true);
              }
            });
          }


        });

        button.setEnabled(false);
        t.start();
      }
Zugegebenermaßen sieht das etwas komplizierter aus. Lassen Sie uns deshalb überlegen, was alles ausgeführt werden soll.
  1. Deaktivieren der Schaltfläche
  2. 10 Sekunden warten
  3. Aktivieren der Schaltfläche
Da die Punkte 1) und 3) Swing-Komponenten manipulieren, müssen die entsprechenden Anweisungen auf dem EDT ablaufen. In Bezug auf 1) ist keine entsprechende Vorkehrung erkennbar, in Bezug auf Punkt 3) aber schon. Mit SwingUtilities.invokeLater() sorgen Sie dafür, dass Anweisungsfolgen Swing-gerecht auf dem EDT ausgeführt werden. Punkt 2) repräsentiert ja gerade die Hintergrundverarbeitung und soll deshalb auf einem eigenen Thread abgearbeitet werden. Taushcen Sie doch einmal die Methodenimplementierung aus und starten das Programm. Während in der (Eclipse-)Konsole von 1 bis 10 gezählt wird, können Sie problemlos die Größe des Fensters verändern. Auch die Schaltfläche wird korrekt (de)aktiviert. Also alles in Ordnung. …oder?
Wie Sie wissen, habe ich beim Deaktivieren der Schaltfläche nicht ausdrücklich sichergestellt, dass die Manipulation der Komponente auf dem EDT stattfindet. Offenbar klappt das aber sehr gut. Bitte packen Sie in die main()-Methode der Klasse mal folgende Anweisung: System.out.println(EventQueue.isDispatchThread());
Wenn Sie die Anwendung erneut starten, sehen Sie sehr wahrscheinlich den Wert false. Dass das Programm also gut funktioniert, ist mehr oder weniger Zufall. Auch die Initialisierung des Fensters muss auf dem EDT laufen! Deshalb gehört ein einsprechender invokeLater()-Aufruf als erste und einzige Anweisung in die main()-Methode.

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        Toolkit.getDefaultToolkit().setDynamicLayout(true);
        System.out.println(EventQueue.isDispatchThread());
        new Better().setVisible(true);
      }
    });
  }
Merke: nur weil es den EDT gibt, bedeutet dies nicht, dass ein Programm darauf läuft.

1 comment:

  1. Wer sich für die Fehlersuche in Swing-Anwendungen interessiert, sollte sich den Artikel Debugging Swing von Kirill Grouchnikov ansehen.

    ReplyDelete