2011-12-15

Ultimate Swing, Teil 7

Beim Schreiben einer Swing-Anwendung müssen Sie prüfen, welche Operationen Sie auf dem EDT ausführen lassen, und welche besser in einem eigenen Thread abgearbeitet werden. Die bisherigen Schlüsselerkenntnisse nochmals kurz zusammengefasst:

  • alle Manipulationen von Bedienelementen finden immer auf dem EDT statt
  • alles was länger als 200 Millisekunden dauert, läuft in einem eigenen Thread.

Sicher fragen Sie sich, wie ich gerade auf 200 Millisekunden komme. Natürlich ist dies ein künstlicher Wert, mit dem ich vor allem unterstreichen möchte, dass Sie unter allen Umständen die Oberfläche reaktionsbereit halten müssen. Egal, ob Sie auf die Daten eines Wetter-Webservice warten oder eine Datei in das lokale Dateisystemschreiben – es ist inakzeptabel, dass die Benutzeroberfläche für einen spürbaren Zeitraum zu hängen scheint.

Wenn Sie jetzt sagen, dass dies nach viel Arbeit riecht, so haben Sie im Prinzip recht. Der Code, der erforderlich ist, um Operationen auf dem richtigen Thread ablaufen zu lassen, fällt aber geringer aus, als es nach meinen letzten beiden Beispielen vielleicht scheint. Bitte sehen Sie sich die Klasse WorkerDemo1 an.

public class WorkerDemo1 extends JFrame {

  public WorkerDemo1() {
    super("WorkerDemo1");
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    JPanel cp = new JPanel(new BorderLayout());
    cp.add(new JLabel(
        "bitte das Fenster nach Belieben vergrößern und verkleinern"),
        BorderLayout.NORTH);
    cp.add(new JPanel() {
      @Override
      protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.red);
        g.drawLine(0, 0, getWidth() - 1, getHeight() - 1);
        g.drawLine(0, getHeight() - 1, getWidth() - 1, 0);
      }
    }, BorderLayout.CENTER);
    final JButton button = new JButton("Hintergrundaktion durchführen");
    button.addActionListener(new ActionListener() {

      public void actionPerformed(ActionEvent e) {
        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {

          @Override
          protected Void doInBackground() throws Exception {
            for (int i = 1; i <= 10; i++) {
              try {
                Thread.sleep(1000);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
              System.out.println(i);
            }
            return null;
          }

          @Override
          protected void done() {
            button.setEnabled(true);
          }
        };
        button.setEnabled(false);
        worker.execute();
      }
    });
    cp.setPreferredSize(new Dimension(400, 300));
    cp.add(button, BorderLayout.SOUTH);
    setContentPane(cp);
    pack();
    setLocationRelativeTo(null);
  }

  public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
      @Override
      public void run() {
        Toolkit.getDefaultToolkit().setDynamicLayout(true);
        System.out.println(EventQueue.isDispatchThread());
        new WorkerDemo1().setVisible(true);
      }
    });
  }
}

Da sich die relevanten Änderungen auf die Methode actionPerformed() konzentrieren, zeige ich Ihnen diese noch einmal gesondert:

      public void actionPerformed(ActionEvent e) {
        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {

          @Override
          protected Void doInBackground() throws Exception {
            for (int i = 1; i <= 10; i++) {
              try {
                Thread.sleep(1000);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
              System.out.println(i);
            }
            return null;
          }

          @Override
          protected void done() {
            button.setEnabled(true);
          }
        };
        button.setEnabled(false);
        worker.execute();
      }

In der Methode geschieht folgendes:

  1. Instantiieren eines Objekts vom Typ SwingWorker
  2. Deaktivieren der Schaltfläche
  3. Aufruf der Methode execute()

Durch Aufruf von execute() wird eine Hintergrundaktivität gestartet. Was hier passiert, legen Sie durch Ihre Implementierung der Methode doInBackground() fest. Aufräumarbeiten nach Beendigung dieser Aufgaben packen Sie in den Methodenrumpf von done().

No comments:

Post a Comment