Swing ‚beschleunigen‘ mit Foxtrot

Situation:

Eine Aussage mit der man als Java-Entwickler im Client-Bereich immerwieder konfrontiert wird ist, „Swing ist langsam.“. Selbst Java-Entwickler behaupten dies gerne, vorzugsweise wenn sie mit SWT arbeiten. Nun, etwas wahres ist schon dran. Schaut man sich viele Swing-Anwendungen einmal an, auch und gerade einfache Beispielanwendungen aus diversen Tutorials und Büchern, so stellt man schnell fest, dass sich viele davon langsam anfühlen. Dieser subjektive Eindruck beruht auf der Beobachtung von Buttons, die nicht oder nur träge reagieren, langsamen Neuzeichnen von UI-Komponenten und kurzzeitigem Einfrieren kompletter UIs.

Ursache:

Die häufigste Ursache für dieses Verhalten liegt in der Architektur von Swing und ihrer Nichtbeachtung beim Programmieren. Da die wenigsten Programmierer zu Beginn gleich alles an Hintergrund-Infos lesen, dessen sie habhaft werden können, sondern lieber anhand einfacher kleiner Beispiele lernen und erste Erfolgserlebnisse haben, ist das Wissen um eben diese Architektur oft unterentwickelt. So ist es eher die Regel als die Ausnahme, dass beim Auswählen eines Menüpunkts oder dem Drücken eines Buttons erstmal einen mehr oder minder langen Moment nichts geschieht, weil der Programmierer im ActionListener erst Daten aus Dateien, von einem Server, aus einer Datenbank, … lädt oder sonstige relativ zeitaufwändige Operationen durchführt. Diese sind es, die ein Swing-UI zum erlahmen bringen.

Das Zeichnen von Komponenten und die Abarbeitung von Events erledigt Swing im AWT Event Dispatcher Thread, kurz EDT. Das führt zu dem Effekt, dass alles, was wir in unseren Event-Listenern tun, die Abarbeitung von weiteren Events und das Neuzeichnen von Komponenten so lange unterbindet, bis unser Code abgearbeitet wurde.

Lösungsansätze:

Das Problem lässt sich lösen, indem man zeitintensive Operationen in den Listenern in eigene Threads auslagert. Unglücklicherweise ist Multithreading mit diversen Fallstricken verbunden. Daher meidet man naturgemäß die Verwendung von Threads, wo es eben geht, um nicht ein Problem durch ein anderes zu ersetzen – zumal Bugs im Zusammenhang mit Multithreading oft nur schwer nachvollziehbar und auffindbar sind.

Das Swing-Team von Sun selbst ersann schon vor einigen Jahren eine Möglichkeit, dieses häufig auftretende Problem zu lösen. Heraus kam eine Klasse namens SwingWorker. Vielen ist diese Klasse aber unbekannt, da sie auch in der Literatur oftmals keine Erwähnung findet, vermutlich schlicht und ergreifend weil sie nicht Bestandteil der Klassenbibliothek ist. Mit dem kommenden Java 6 (derzeit noch in der Beta 2) wird diese Klasse dann auch endlich fester Bestandteil von Java werden – besser spät als nie.

Auch an anderer Stelle machte man sich Gedanken über eine allgemeingültige und einfache Lösung für einen Sachverhalt, der in praktisch jeder Swing-Anwendung vorkommt. Heraus kam mit Foxtrot eine kleine aber feine frei verfügbare Bibliothek, die einmal verstanden viele Kopfschmerzen und viel Unmut ersparen kann. Ich stolperte in einem mittlerweile schon älteren Webcast über Foxtrot und war selbst erstaunt dass mir diese Library bis dato nie untergekommen war. Minuten später testete ich sie aus und kurz drauf stellte ich mehrere meiner Anwendungen auf der Arbeit auf Foxtrot um. Foxtrot erlaubt es denkbar einfach zeitintensiven Code in einer Swing-Anwendung in eigene Threads zu verpacken und ohne dass man sich um Synchronisation und konkurrierende Zugriffe kümmern müsste, seine Anwendungen fühlbar schneller zu machen. Endlich ist es nun auch kein Problem mehr dem Nutzer ein visuelles Feedback darüber zu geben, was die Anwendung denn gerade im Hintergrund macht. Ehe man eine Operation in einem Foxtrot Thread startet, manipuliert man sein UI entsprechend (schaltet Buttons inaktiv, setzt „Daten werden geladen“-Nachrichten in Models, …) und schon ist die Anwendung deutlich benutzerfreundlicher, weil sie nicht einfach nur arbeitet, sondern weil sie den Benutzer darüber informiert, dass sie arbeitet und dies in einer Art & Weise tut, die dem Benutzer nicht so vorkommt als sei die Software nicht sauber programmiert.

Fazit:

Mittlerweile ist Foxtrot Bestandteil aller meiner Swing-Anwendungen und sie fühlen sich nun alle deutlich flotter an. Dieser Eindruck ist auch weiterhin subjektiv, sollte aber nicht unterschätzt werden. Keiner arbeitet gerne mit Anwendugen, die einem irgendwie unfertig und langsam vorkommen – ganz egal ob sie dies quantitativ gemessen auch sind.
Mittlerweile habe ich Eclipse im Templates für die Verwendung von Foxtrot erweitert. Nachteilig ist anfangs, dass man sich einige zusätzliche Einrückungen einhandelt und diese den Code optisch weniger ansprechend und etwas unübersichtlicher machen können. Diesem Umstand kann man begegnen indem man auf die Verwendung von anonymen Foxtrot Jobs und Tasks verzichtet und diese statt dessen als finale innere Klassen deklariert. Meine Swing UIs swingen nun Foxtrot und es ist eine dieser Entdeckungen bei der man sich unwillkürlich fragt, warum die Jungs bei Sun nicht drauf gekommen sind und dies gleich integriert haben.
Da ich aber weiß, dass Swing ursprünglich in nur 90 Tagen entwickelt wurde, will ich mal nicht zu kritisch sein…

Links:

Kommentare (2)

  1. sehr geehrter Herrn Langer,
    ich danke Ihnen im vorraus für Ihren Beitrag im Web zum Foxtrot.
    ich schreibe meine Diplom Arbeit über Java RMI und mein Programm hat Swing GUI ich habe dieses Problem mit dem eingefrorenen GUI und bin froh dass ich auf foxtrot gestossen bin denn Swingworker vo Sun hilft mir nicht weiter.
    nun meine Frage : ich benutze Eclipse was aber nicht wie ich Foxtrot darin einbenden kann.
    ich würde mich sehr freuen ,wenn Sie mir ein paar Tips geben würden.
    mit freudlichen Grüssen!
    Danke.

  2. Hi!

    Das Einbinden von foxtrot in ein Projekt in Eclipse ist recht einfach. Es gibt mehrere Wege, ich beschreibe mal kurz den schnellsten davon:

    – öffne dein Projekt in Eclipse
    – erzeuge einen neuen Unterordner „lib“ (Name ist frei wählbar) auf gleicher Höhe wie den „src“ Ordner
    – entpacke das foxtrot Archiv, welches du von Sourceforge heruntergeladen hast
    – kopiere aus dem Verzeichnsi „lib“ des Archivs die Datei foxtrot.jar in das „lib“ Verzeichnis deines Projekts, unter Windows kannst du das per Drag&Drop vom Windows Explorer nach Eclipse machen
    – markiere die foxtrot.jar in Eclipse mit der Maus, drück die rechte Maustaste und wähle aus dem Menü „Build Path“ -> „Add to Build Path“ aus

    Nun kannst du foxtrot in deinem Projekt benutzen.

    P.S.:
    Ich habe von jeher die Beta1 von foxtrot 3.0 verwendet und hatte damit nie Probleme.

Kommentare sind geschlossen.