JProgressbar und Multi-Threading
Einführung
Im Gegensatz zu anderen Swing Komponenten ist der Einsatz des JProgressbar als Fortschrittsanzeige aufwändig zu implementieren. Der Grund dafür ist der Einsatz von Multi-Threads (mehreren eigenständigen Prozessen) welche die Applikation schnell unübersichtlich und fehleranfällig machen können.
Ein zeitintensiver Rechenvorgang mit Fortschrittsanzeige kann auch ohne Multi-Thread Einsatz, nur über den
Event Dispatch Thread (der für das Zeichnen des GUI zuständig ist) abgearbeitet werden. Allerdings wäre dann der EDT durch die aufwändige Operationen blockiert und das GUI würde einfrieren. Deshalb gilt:
- Zeitintensive Vorgänge sollten nicht über den Event Dispatch Thread ausgeführt werden, da die Applikation einfriert.
- Auf Swing Kompnenten sollte nur über den Event Dispatch Thread zugegriffen werden.
Da die Fortschrittsanzeige ein GUI-Aspekt ist und synchron zum Fortschritt, der in den meisten Fällen ein zeitintensiver Vorgang ist, ausgeführt werden soll, muss ein zweiter Thread erstellt werden.
Multi-Thread erstellen
EDT
Alle GUI-Komponenten werden also weiterhin über den EDT benutzt. Auch die JProgressbar, die den Fortschritt des Prozesses visualisiert, wird über den EDT benutzt.
Für die Ausführung des zeitintensiven Prozess wird ein neuer Thread erstellt, dem beliebige Parameter mitgegeben werden.
private void initialSendButtonModel() {
fSendButtonModel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final JProgressBar progressBar = new JProgressBar(0, fTask.getSize());
progressBar.setValue(0);
progressBar.setStringPainted(true);
final JDialog progressDialog = FrameFactory.createDialog("Fortschritt");
progressDialog.getContentPane().add(progressBar);
final Worker worker = new Worker(fTask, progressDialog);
worker.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
int progress = worker.getProgress();
if (progress == 0) {
progressBar.setIndeterminate(true);
} else {
progressBar.setIndeterminate(false);
progressBar.setValue(progress);
}
}
});
worker.execute();
progressDialog.setVisible(true);
}
});
}
SwingWorker
Das Handling von Threads ist seit Verion 6.0 (auch 1.6 genannt) einfacher geworden, durch die Funktionen der SwingWorker Klasse. Beim erstellen eines eigenen Threads kann von der Klasse SwingWorker abgeleitet werden. Daraufhin muss die Methode doInBackground implementiert werden.
Sobald die finale Methode execute (die aufgrund der Vererbung durch SwingWorker, über die neue Instanz zugänglich ist) aufgerufen wird, wird die Methode doInBackground ausgeführt.
In dieser Methode muss der zeitaufwändige Prozess getätigt werden. Dabei wir der Fortschritt mit der Methode setProgress erhöht. Durch den Aufruf von setProgress wird der Listener für den Thread notifiziert und die propertyChange Methode aufgerufen, die den Wert der Progressbar erhöht. Dies geschieht wiederum im EDT.
public class Worker extends SwingWorker<Void, Void> {
private final JDialog fProgressDialog;
private final MyTask fTask;
public Worker(MyTask task, JDialog progressDialog) {
fTask = task;
fProgressDialog = progressDialog;
}
protected Void doInBackground() {
for (int i = 0; i < fTask.getSize(); ++i) {
fTask.doTaskWithNummer(i);
setProgress(i);
}
return null;
}
protected void done() {
JOptionPane.showMessageDialog(FrameFactory.getMainFrame(),
"<html>Der Prozess <b>" + fTask.getName() + "</b> wurde erfolgreich durchgeführt.</html>",
"Prozess erfolgreich beendet",
JOptionPane.INFORMATION_MESSAGE);
fProgressDialog.dispose();
}
}
Zusammenfassung
Durch den Aufruf der execute Methode wird der zeitintensive Prozess fast synchron zum Fortschrittsbalken ausgeführt. Der Dialog wiederum blockiert während dieser Zeit den Zugriff auf das Haupt-Fenster, ist aber immer noch aktiv für Benutzerinteraktionen.
Literatur