invokeLater - Wann?

taouri

Mitglied
Hallo zusammen,

könnte mir bitte mal jemand erklären, wann genau man invokeLater (oder auch invokeAndWait) wirklich einsetzen muss. Ich habs leider noch immer nicht begriffen, trotz vielem Java-Tutorial lesen. Auch die Java Insel konnte mir nicht weiterhelfen. Muss ich z.B. generell in einem Eventhandler invokeLater benutzen, oder jedesmal wenn ich die GUI anspreche? Welche Befehle kann man zusammen in einen Thread hauen? Wann brauche ich einen einzelnen?

Ich hoffe jemand kann mir helfen

Gruß

taouri
 
Hallo taouri,

ich versuche es dir zu erläutern soweit wie ich es verstanden habe. Man möge mich bei Bedarf gern verbessern.

Alles was von der main() aus gestartet und abgearbeitet wird, wird grundsätzlich im EventDispatchThread (Ereignisverlauf) ausgeführt, ausser wir erzeugen explizit einen neuen Thread und führen darin Code aus. Die GUI wird ebenfalls im EventDispatchThread aufgebaut und aktualisiert. Die Interaktionen, mit Events und Listenern werden auch vom EDT ausgeführt.

Wenn wir aber nun einen "rechenlastigen", "länger dauernden" Code im EDT ausführen verhindert dieser das die GUI aktualisiert wird und die GUI auf Benutzerinteraktionen reagiert. Abhilfe kann man hier schaffen durch verlagern von "langem Code" in neue Threads. Somit arbeitet dieser parallel zum EDT und blockiert diesen nicht. D.h. die GUI wird aktualisiert, kann wieder reagieren, im Hintergrund aber werden parallel anderweitiges ausgeführt.

Für was aber brauchen wird invokelater und invokeandwait? Du möchtest bspw. alle Primzahlen bis 10000 berechnen, dabei sollen die neuen Werte sofort in der GUI sichtbar sein, also eine ständige Aktualisierung. Wie wir oben schon gelernt haben, verlagen wir diese rechenintensive arbeit in einen externen Thread. Wir möchten bspw. nach jedem (for)Schleifendurchgang in dem Thread die GUI aktualisieren und die neuen Werte anzeigen. Würden wir den Code zur Aktualisierung in den neuen Thread einfach so mitreinschreiben würde dieser nicht im EDT ausgeführt werden, welches wir aber wünschen. Dazu ist ja der EDT da.

Also schreiben wir den Code der zur GUIaktualisierung doch in den neuen Thread aber in invokelater(). Dieser wird dann der EDT Warteschlange mitgegeben und dort ausgeführt. Invokeandwait wird eigentlich weniger benutzt, auf den Unterschied kann ja noch jemand anderes eingehen.

Das war die Theorie der Dinge, wie ich sie hier und dort und da desöfteren gelesen hatte!

Java:
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;

import javax.swing.*;

public class ProgressDemo2 extends JFrame implements ActionListener {

	private static int max = 555;

	private int i;
	
	private String fac;

	private JButton btn = new JButton("Calculate all factorials till ");

	private JTextField tf = new JTextField(4);

	private JPreviewDialog previewDialog = new JPreviewDialog(this);

	public ProgressDemo2() {
		super("Progress Demo");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setLocationByPlatform(true);
		this.setAlwaysOnTop(true);

		btn.addActionListener(this);
		tf.setText("" + max);

		this.setLayout(new FlowLayout());
		this.add(btn);
		this.add(tf);

		this.pack();
		this.setVisible(true);
	}

	public static void main(String[] args) {
		new ProgressDemo2();
	}

	public void actionPerformed(ActionEvent e) {

		try {
			int temp = Integer.valueOf(tf.getText());
			max = temp;
		} catch (NumberFormatException nfe) {
			System.out.println(nfe.getMessage());
		}

		previewDialog.clearTextArea();

		new Thread() {
			public void run() {
				previewDialog.showDialog();

				for (i = 0; i <= max; i++) {
					fac = ProgressDemo2.this.getFactorial(i).toString();

// Variante 1					
//					{
//					previewDialog.updateTextArea("factorial of " + i + ": "
//							+ fac
//							+ "\n");
//					}

// Variante 2					
//					{
//					SwingUtilities.invokeLater(new Runnable() {
//						public void run() {
//							previewDialog.updateTextArea("factorial of " + i + ": "
//									+ fac
//									+ "\n");
//						}
//					});
//					}

// Variante 3					
					try {
						SwingUtilities.invokeAndWait(new Runnable() {
							public void run() {
								previewDialog.updateTextArea("factorial of " + i + ": "
										+ fac
										+ "\n");
							}
						});
					} catch (InterruptedException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				
				}
			}
		}.start();
	}

	public BigInteger getFactorial(long n) {
		BigInteger bi = new BigInteger("1");
		if (n == 0)
			return bi;
		for (long i = 1; i <= n; i++) {
			bi = bi.multiply(new BigInteger("" + i));
		}
		return bi;
	}

	class JPreviewDialog extends JDialog {

		private JTextArea tArea = new JTextArea();

		public JPreviewDialog(JFrame owner) {
			super(owner, "MyDialog", false);
			this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
			this.setLocationByPlatform(true);
			this.setSize(640, 480);

			tArea.setEditable(false);

			this.add(new JScrollPane(tArea), BorderLayout.CENTER);
			this.add(btn, BorderLayout.SOUTH);
		}

		public void showDialog() {
			this.setVisible(true);
		}

		public void updateTextArea(String s) {
			tArea.append(s);
			tArea.setCaretPosition(tArea.getText().length());
		}

		public void clearTextArea() {
			tArea.setText("");
		}
	}
}

Die Praxis hat mich wirklich überrascht. In dem Thread sind drei alternative Implementierungsmöglichkeiten. Einfach 2 Auskommentieren und einen belassen. Es sind wirklich signifikante Unterschiede zu sehen.

Das Ergebnis aus diesem Beispiel ist folgende:
Alternative 1: Das Resultat ist akzeptabel.
Alternative 2: Fehlerhafte aktualisierung.
Alternative 3: Widererwarten (weil davon immer abgeraten wird) bestes Resultat.


Vg Erdal
 
Zurück