Downloads
| SwingWorker Tutorial |
| Monday, 10 March 2008 01:02 |
|
Jeder kennt folgendes Problem: Man arbeitet mit einer Anwendung, führt eine Aktion aus und plötzlich reagiert die GUI nicht mehr. Das Programm scheint "eingefroren" zu sein. Nach einiger Zeit reagiert das Programm wieder und die gewünschte Aktion wurde ausgeführt. Grund hierfür kann sein, dass die Aktion im sog. Event Dispatcher Thread (EDT) ausgeführt wurde, also dem Thread der auch für die GUI zuständig ist. Ist die Aktion rechen- und zeitaufwändig so können zwischenzeitlich keine anderen Aktionen, wie das Verschieben des Programmfensters, ausgeführt werden. Das Programm "hängt". Um das Problem zu lösen hat Java seit Version 6 den sog. SwingWorker integriert. Dadurch lassen sich rechen- und zeitaufwändige Prozesse auslagern und blockieren so nicht mehr den EDT. Mit Hilfe eines Beispiels zeigt dieses Tutorial den Einsatz des SwingWorkers. Benötigt wird zwingend Java 6. Im folgenden Beispiel wird ein simples Programm mit graphischer Oberfläche erstellt. Aufgabe des Programms ist es, eine vorgegebene Primzahl zu berechnen. Beispielsweise ist die fünfte Primzahl 11 (2,3,5,7,11). Die Funktionsweise der Berechnung ist für das Tutorial unwichtig; Die Primzahlenberechnung wurde gewählt, da die Berechnung sehr rechen- und zeitintensiv sein kann (vor allem wenn sie so ineffizient programmiert wurde wie hier ;-)). Zuerst das Programm ohne SwingWorker (Download: Demo1.java ): import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; public class Demo1 extends JFrame implements ActionListener{ JLabel jlabe_text,jlabe_result; JTextField jtext_input; JButton jbutt_calc; /** * Init the GUI elements */ public void initComponents(){ jlabe_text = new JLabel("Calculate prime:"); jtext_input = new JTextField(); jlabe_result = new JLabel(); jbutt_calc = new JButton("Calc"); jbutt_calc.addActionListener(this); jbutt_calc.setActionCommand("calc"); getContentPane().setLayout(new GridLayout(1,4)); getContentPane().add(jlabe_text); getContentPane().add(jtext_input); getContentPane().add(jbutt_calc); getContentPane().add(jlabe_result); pack(); setSize(400,60); setVisible(true); } public void actionPerformed(ActionEvent evt){ String cmd = evt.getActionCommand(); if(cmd.equalsIgnoreCase("calc")){ // calc button pressed // Calculate the result without a SwingWorker // This will freeze the GUI String result = getXPrime(Integer.parseInt(jtext_input.getText())); jlabe_result.setText(result); } } public static void main(String[] args) { new Demo1().initComponents(); } /** * The time consuming method. For this example it's not important * how this method works. * Given a number n this method calculates the n th prime * e.g. given n = 5 the 5th prime is 11 * @param the n th prime to be calculated * @return the prime */ public String getXPrime(int n){ int primes = 1; int zahl = 2; while(primes != n){ zahl++; for(int i=2;i<zahl;i++){ if(!(zahl%i==0) && i+1 == zahl){ primes++; i = zahl+1; } else if(zahl%i==0){ i = zahl+1; } } } return String.valueOf(zahl); } }
Führt man das Programm aus und berechnet eine relativ kleine Primzahl (z.B. die 10. Primzahl) so fällt nicht auf, dass die Berechnung im EDT läuft. Berechnet man jedoch beispielsweise die 10000. Primzahl so "friert" die GUI kurzzeitig ein. Das macht sich dadurch bemerkbar, dass der Knopf eingedrückt bleibt und auch keine Werte ins Textfeld eingegeben werden können.
(Download: Demo1WithSwingWorker.java): import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.concurrent.ExecutionException; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JTextField; import javax.swing.SwingWorker; public class Demo1WithSwingWorker extends JFrame implements ActionListener{ JLabel jlabe_text,jlabe_result; JTextField jtext_input; JButton jbutt_calc; SwingWorker<String, Void> worker; String theresult; /** * Init the GUI elements */ public void initComponents(){ jlabe_text = new JLabel("Calculate prime:"); jtext_input = new JTextField(); jlabe_result = new JLabel(); jbutt_calc = new JButton("Calc"); jbutt_calc.addActionListener(this); jbutt_calc.setActionCommand("calc"); getContentPane().setLayout(new GridLayout(1,4)); getContentPane().add(jlabe_text); getContentPane().add(jtext_input); getContentPane().add(jbutt_calc); getContentPane().add(jlabe_result); pack(); setSize(400,60); setVisible(true); } public void actionPerformed(ActionEvent evt){ String cmd = evt.getActionCommand(); if(cmd.equalsIgnoreCase("calc")){ // calc button pressed // Construct a new SwingWorker worker = new SwingWorker<String, Void>(){ @Override protected String doInBackground(){ return getXPrime(Integer.parseInt(jtext_input.getText())); } @Override protected void done(){ try { jlabe_result.setText(get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }; // Execute the SwingWorker; the GUI will not freeze worker.execute(); } } public static void main(String[] args) { new Demo1WithSwingWorker().initComponents(); } /** * The time consuming method. For this example it's not important * how this method works. * Given a number n this method calculates the n th prime * e.g. given n = 5 the 5th prime is 11 * @param the n th prime to be calculated * @return the prime */ public String getXPrime(int n){ int primes = 1; int zahl = 2; while(primes != n){ zahl++; for(int i=2;i<zahl;i++){ if(!(zahl%i==0) && i+1 == zahl){ primes++; i = zahl+1; } else if(zahl%i==0){ i = zahl+1; } } } return String.valueOf(zahl); } } Wie man auf folgendem Screenshot sehen kann ist die GUI nach Drücken des Knopfes weiterhin nutzbar.
Das wars! |


ich wäre dankbar für eine erweiterung des tutorials, was den Umgang mit den PropertyChangeListenern angeht. Wie sähe das Beispiel aus, wenn ich noch eine JProgressBar mit einbauen will?
JProgressBar sollte bei N=20 beispielsweise bei der ersten gefundenen zahl auf 5% springen, sobald 10 Zahlen gefunden wurden entsprechend auf 50% etc.
Danke und Gruß
Chris
Lesbarkeit erhöhen:
Firebug -> CSS "template_css.css":
body {
background-color: #FFFFFF;
color: #000000;
font-family: Times New Roman;
font-size: 140;
}
;)
Vielen Dank dafür!
Vielen Dank.
nach langem suchen endlich eins gefunden, dass man sofort verstehen kann. Danke, saubere Arbeit!