Wie in anderen UI Frameworks (wie Swing oder Swt) gibt es auch in jowidgets einen UI Thread, in welchem alle UI Events dispatched werden. Alle Modifikationen an Widgets oder deren Models müssen in diesem Thread erfolgen. Reagiert man auf Events, welche durch Nutzereingaben ausgelöst wurden, befindet man sich bereits automatisch im UI Thread. Dabei ist, wie auch bei anderen UI Frameworks, darauf zu achten, den UI Thread nicht für lange Berechnungen zu blockieren, weil ansonsten keine Events mehr verarbeitet werden können und die Oberfläche für diese Zeit einfriert.
Operationen die potentiell lange dauern können, wie Beispielsweise das Aufrufen eines Service, sollten daher immer in einem eigenen Thread stattfinden.
Will man aus einem anderen Thread heraus wieder Methoden auf UI
Elementen aufrufen, bietet die Schnittstelle
IUiThreadAccess
dafür die folgenden Methoden:
void invokeLater(Runnable runnable); void invokeAndWait(Runnable runnable) throws InterruptedException;
Die Methode invokeLater()
führt das übergebene
Runnable
zu einem späteren Zeitpunkt im Ui
Thread aus. Nachdem die Methode zurückkehrt, kann man jedoch nicht
davon ausgehen, dass das Runnable bereits ausgeführt wurde.
Die Methode invokeAndWait()
führt auch das
Runnable im Ui Thread aus, blockiert aber so lange, bis das
Runnable
ausgeführt wurde. Diese Methode ist
mit höchster Vorsicht zu verwenden. Bei falscher Verwendung
erzeugt man dadurch sehr schnell einen
Deadlock. Statt blockierenden
Methodenaufrufen sollte man besser mit Callback’s arbeiten.
Beide Methoden können in jedem beliebigen Thread aufgerufen werden.
Um zu prüfen, ob der aktuelle Thread der UI Thread ist, kann man die folgende Methode verwenden:
boolean isUiThread();
Der Aufruf:
IUiThreadAccess uiThreadAccess = Toolkit.getUiThreadAccess();
muss immer im UI Thread erfolgen. Dies liegt unter anderem daran, dass es im Web Kontext mehrere UI Threads (einer pro User Session) existieren, und ein beliebiger Thread nicht wissen kann, welcher UI Thread verwendet werden soll.
In der Praxis übergibt man die IUiThreadAccess
Instanz einfach an den anderen Thread. Folgendes Beispiel zeigt,
wie man das einfach mit Hilfe des Ausführungsstacks machen kann:
1 //Add an listener to the button that does a long lasting calculation 2 button.addActionListener(new IActionListener() { 3 @Override 4 public void actionPerformed() { 5 6 //this will be invoked in the ui thread 7 final IUiThreadAccess uiThreadAccess = Toolkit.getUiThreadAccess(); 8 9 //execute the service calculation in an service thread 10 executorService.execute(new Runnable() { 11 12 @Override 13 public void run() { 14 //this will be invoked in the service thread 15 final ServiceResult result = longLastingService.doLongLastingCalculation(); 16 17 uiThreadAccess.invokeLater(new Runnable() { 18 @Override 19 public void run() { 20 //this will be executed in the ui thread 21 serviceResultWidget.showResult(result); 22 } 23 }); 24 } 25 26 }); 27 } 28 });
Hinweis: Das obige Beispiel wurde bewusst vereinfacht. Es fehlen u.A. wichtige Aspekte wie Fehlerbehandlung oder das Abbrechen der Berechnung.