Ich weiss, das dieses Thema hier schon mehr als einmal behandelt wurde. Ich habe auch die letzten Tage ziemlich viele Beiträge zu dem Thema gelesen, komme aber trotzdem nicht so recht weiter.
Also, zu meinem Problem: Ich habe ein Applet welches per Button-Klick Daten vom Server holen soll. Dies kann sehr lange dauern. Aus diesem Grund will ich in der Zwischenzeit eine Progressbar anzeigen. Ich weiss, dass ich dazu mehrere Threads brauche. Mein Problem ist nun, dass die Progressbar erst dann angezeigt wird, wenn mein Request fertig abgearbeitet ist! Das Fenster, in dem sich die Progressbar befindet wird zwar sofort gemalt, ist aber 'leer'. Sobald dann der Request abgearbeitet ist, fängt dann an die Progressbar loszulaufen.
Hier ein paar Code-Fragmente:
Der folgende Code ist aus dem Thread hier übernommen & leicht angepaßt.
Also, zu meinem Problem: Ich habe ein Applet welches per Button-Klick Daten vom Server holen soll. Dies kann sehr lange dauern. Aus diesem Grund will ich in der Zwischenzeit eine Progressbar anzeigen. Ich weiss, dass ich dazu mehrere Threads brauche. Mein Problem ist nun, dass die Progressbar erst dann angezeigt wird, wenn mein Request fertig abgearbeitet ist! Das Fenster, in dem sich die Progressbar befindet wird zwar sofort gemalt, ist aber 'leer'. Sobald dann der Request abgearbeitet ist, fängt dann an die Progressbar loszulaufen.
Hier ein paar Code-Fragmente:
Code:
public static RequestError sendRequest( RequestObject oRequestObject ) {
RequestProgressBar x = new RequestProgressBar();
x.setVisible(true);
x.start();
RequestError oError = sendRequest(oRequestObject); // Erst nach dieser Anweisung erfolgt die Anzeige!
return oError;
}
Der folgende Code ist aus dem Thread hier übernommen & leicht angepaßt.
Code:
import java.lang.reflect.InvocationTargetException;
import java.awt.*;
import javax.swing.*;
public class RequestProgressBar extends JFrame implements Runnable {
/** Der Algorithmus, der ausgeführt werden soll */
private Algorithmus algorithmus;
/** Der Fortschrittsbalken */
private JProgressBar progress;
/**
* Standartkonstruktor, stellt ein neues Fenster her.
*/
public RequestProgressBar(){
progress = new JProgressBar();
algorithmus = new Algorithmus(); // Wir brauchen ja einen Algorithmus ;-)
// Der folgende Teil ist nur für das Auge, eine Benutzeroberfläche die
// etwas dynamisch ist, kommt immer gut an.
Container content = getContentPane();
content.setLayout( new GridBagLayout() );
JPanel panel = new JPanel( new GridLayout( 1, 2 ));
content.add( progress, new GridBagConstraints( 0, 0, 1, 1, 1.0, 1.0,
GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
new Insets( 2, 2, 2, 2 ), 0, 0) );
content.add( panel, new GridBagConstraints( 0, 1, 1, 1, 1.0, 1.0,
GridBagConstraints.SOUTHEAST, GridBagConstraints.NONE,
new Insets( 2, 2, 2, 2 ), 0, 0) );
// Geeignete Grösse und Position wählen
pack();
setDefaultCloseOperation( EXIT_ON_CLOSE );
}
/**
* Startet den Algorithmus, das Verhalten ist nicht definiert, wenn der
* Algorithmus bereits aktiviert wurde.
*/
protected void start(){
// Einen neuen Thread herstellen, und starten.
new Thread( this ).start();
}
/**
* Stoppt den Algorithmus, immer unter der Annahme, dass er zurzeit
* läuft.
*/
protected void stop(){
algorithmus.setInterrupted( true );
}
/**
* Die Methode wird aus einem Zusatzthread aufgerufen. Sie startet
* den Algorithmus, und sorgt nach seiner Beendigung dafür, dass das
* Fenster weiterhin schön aussieht
*/
public void run(){
// Zurzeit ist der Algorithmus nicht unterbrochen
algorithmus.setInterrupted( false );
// Den Algorithmus starten. Die Methode kehrt erst zurück, wenn der
// Algo abgearbeitet oder unterbrochen wurde.
algorithmus.run( progress );
if( algorithmus.isInterrupted() ){
/*
* Sollte der Algorithmus abgebrochen worden sein, wird die
* Progressbar zurück auf den Startwert gesetzt.
*
* Die Methode "invokeLater" garantiert, dass dass der Code des
* übergebenen Runnable's synchronisiert zum Dispatch-Thread
* ausgeführt wird. Dies ist notwendig, weil Swing nicht
* threadsicher ist.
*/
SwingUtilities.invokeLater( new Runnable(){
public void run(){
progress.setMinimum( 0 );
progress.setMaximum( 1 );
progress.setValue( 0 );
}
});
}
else{
/*
* Der Algorithmus wurde korrekt beendet, ein Dialog soll den
* Benutzer unterrichten, dass das Programm nun geschlossen wird.
*
* Die Methode "invokeAndWait" garantiert, dass der Code des
* Runnable-Objektes, welches übergeben wird, synchron zum
* Dispath-Thread ausgeführt wird. Ausserdem kehrt die Methode
* erst dann zurück, wenn eben dieser Code ausgeführt wurde.
*/
try {
SwingUtilities.invokeAndWait( new Runnable(){
public void run(){
JOptionPane.showMessageDialog( RequestProgressBar.this,
"Algorithmus erfolgreich beendet.", "Beendet",
JOptionPane.INFORMATION_MESSAGE );
}
});
} catch (InterruptedException e) {
// Kann ja sein, das hier was schiefgeht...
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
class Algorithmus {
/**
* Dieser Fortschrittsbalken wird benutzt, um den Fortschritt des Algos
* anzuzeigen
*/
private JProgressBar status;
/**
* Dieser Boolean wird auf "true" gesetzt, sollte der Algorithmus abgebrochen
* werden.
*/
private boolean interrupted = false;
public Algorithmus(){
}
/**
* Gibt an, ob der Algorithmus abgebrochen wird, oder wurde.
* @return true, falls der Algorithmus stoppen soll
*/
public boolean isInterrupted(){
return interrupted;
}
/**
* Gibt an, ob der Algorithmus abgebrochen werden soll.
* @param interrupt true, falls der Algorithmus abgebrochen werden soll,
* andernfalls false.
*/
public void setInterrupted( boolean interrupt ){
interrupted = interrupt;
}
/**
* Die Hauptmethode des Algorithmuses, hier geschehen alle wichtigen
* Berechnungen. Leider konnte die Weltformel hier nicht Platz finden,
* deshalb beschränkt sich die Methode auf stupides hochzählen eines
* Integers :-)
* @param progress Der Fortschrittsbalken, welcher den Fortschritt
* des Algos anzeigen wird.
*/
public void run( JProgressBar progress ) {
// Den Fortschrittsbalken speichern wir als Instanzvariable. Das
// hat den Vorteil, dass jede der 1000 Methoden des Algos Zugriff
// auf die Progressbar hat.
status = progress;
int begin = 13;
int end = 65;
int current = begin;
// Minimum und Maximum des Fortschrittbalkens setzen
setMinMax( begin, end );
while( current <= end && !isInterrupted() ){
try {
Thread.sleep( 100 ); // Kurz warten
} catch (InterruptedException e) {}
setValue( current++ ); // Hey! Es ist was passiert!
}
// Instanzvariable auf null setzen, damit der Garbagecollector
// allenfalls Müll entfernen kann
status = null;
}
/**
* Setzt die minimalen und die maximalen Werte des Fortschrittbalkens. Die
* Methode kehrt erst zurück, wenn die Werte tatsächlich gesetzt wurden.
* @param min Das Minimum
* @param max Das Maximum
*/
private void setMinMax( final int min, final int max ){
try {
/*
* Diese Methode wird nicht aus dem Dispatcher-Thread aufgerufen.
* Da Swing nicht threadsicher ist, müssen wir für eine
* Synchronisation sorgen, deshalb wird hier "invokeAndWait" verwendet.
*/
SwingUtilities.invokeAndWait( new Runnable(){
public void run() {
status.setMinimum( min );
status.setValue( min );
status.setMaximum( max );
}
});
} catch (Exception e) {}
}
private void setValue( final int value ){
/*
* Der Algorithmus sollte nicht unnötig angehalten werden, nur damit die
* Progressbar schön aussieht, deshalb "invokeLater".
* Es kann nun aber passieren, dass der Algorithmus abbricht, nachdem
* er "setValue" aufgerufen hat (und damit die Variable "status" auf
* null setzt), das übergebene Runnable aber erst später ausgeführt wird.
* Aus diesem Grund wird ein try-catch-Block benutzt, der die mögliche
* NullPointerException abfängt. Alternativ könnte man mit dem Keyword
* "synchronized" einen Mechanismus bauen, der verhindert dass die Variable
* "status" verändert wird, während wir uns in dieser Methode befinden
* (und dann eine einfache if-Abfrage machen).
*/
SwingUtilities.invokeLater( new Runnable(){
public void run() {
try{
status.setValue( value );
}
catch( NullPointerException ex ){
// silent exception
}
}
});
}
}
}