Verwenden Sie den Threading -Modus des Swingworkers
Für Schwungentwickler ist es sehr wichtig, die Parallelität sorgfältig zu verwenden. Ein gutes Swing -Programm verwendet Parallelitätsmechanismen, um Benutzeroberflächen zu erstellen, die die Antwort nicht verlieren - unabhängig von der Art von Benutzerinteraktion kann das Programm immer darauf reagieren. Um ein reaktionsschnelles Programm zu erstellen, müssen Entwickler lernen, wie man Multithreading im Swing -Framework verwendet.
Ein Swing -Entwickler befasst sich mit den folgenden Arten von Threads:
(1) Initiale Threads führen solche Threads den Initialisierungsanwendungscode aus.
(2) Der Ereignis -Versand -Thread werden hier alle Ereignisverarbeitungscodes ausgeführt. Die meisten Code, die mit dem Swing -Framework interagieren, müssen diesen Thread auch ausführen.
(3) Worker-Threads, auch als Hintergrund-Threads bezeichnet, werden alle zeitaufwändigen Aufgaben ausführen.
Entwickler müssen diese Threads nicht explizit in ihrem Code erstellen: Sie werden von der Laufzeit oder dem Swing -Framework bereitgestellt. Die Aufgabe von Entwicklern besteht darin, diese Threads zu verwenden, um reaktionsschnelle, anhaltende Swing -Programme zu erstellen.
Wie alle anderen Programme, die auf Java -Plattformen ausgeführt werden, kann ein Swing -Programm zusätzliche Threads und Thread -Pools erstellen, die die Verwendung des in diesem Artikel eingeführten Ansatzes erfordert. In diesem Artikel werden die oben genannten drei Threads vorgestellt. Die Diskussion über Arbeiter -Threads umfasst die Verwendung der Klasse von Javax.swing.swingworker. Diese Klasse verfügt über viele nützliche Funktionen, einschließlich Kommunikation und Zusammenarbeit zwischen Worker -Thread -Aufgaben und anderen Thread -Aufgaben.
1. Anfangsfaden
Jedes Programm generiert zu Beginn der Anwendungslogik eine Reihe von Threads. In einem Standardprogramm gibt es nur einen solchen Thread: Dieser Thread nennt die Hauptmethode in der Hauptklasse des Programms. In einem Applet ist der anfängliche Thread ein Konstruktor des Applet -Objekts, der die Init -Methode aufruft. Diese Aktionen können in einem einzigen Thread oder in zwei oder drei verschiedenen Threads ausgeführt werden, je nach der spezifischen Implementierung der Java -Plattform. In diesem Artikel nennen wir diese Art von Thread -Anfangsthreads.
In einem Swing -Programm gibt es im ersten Thread nicht viel zu tun. Ihre grundlegendste Aufgabe ist es, ein Runnable -Objekt zu erstellen, das die GUI initialisiert und die Objekte, die zum Ausführen von Ereignissen im Ereignis -Versand -Thread verwendet wurden, initialisiert. Sobald die GUI erstellt wurde, wird das Programm hauptsächlich von GUI -Ereignissen angetrieben, von denen jedes die Ausführung eines Ereignisses im Ereignisversandsthread verursacht. Programmcode kann zusätzliche Aufgaben in ereignisgesteuerte Threads (vorausgesetzt, dass sie schnell ausgeführt werden, damit sie nicht die Verarbeitung von Ereignissen beeinträchtigen) oder Worker-Threads (zur Ausführung zeitaufwändiger Aufgaben verwendet).
Eine anfängliche Thread -GUI -Erstellung von Thread -Orchestrierungs -GUI erfolgt, indem Sie javax.swing.swingutilities.invokelater oder javax.swing.swingutilities.invokeandwait anrufen. Beide Methoden nehmen einen einzigartigen Parameter an: Runnable wird verwendet, um neue Aufgaben zu definieren. Der einzige Unterschied zwischen ihnen ist: Invokerlater orchestriert nur die Aufgabe und kehrt zurück; Invokeandwait wartet, bis die Aufgabe vor der Rückkehr ausgeführt wird.
Siehe das folgende Beispiel:
SwingUtilities.invokelater (new Runnable ()) {public void run () {CreateAndshowgui (); }} Im Applet muss die Aufgabe des Erstellens der GUI in die Init -Methode eingebracht werden und invokeandwait verwenden. Andernfalls ist der erste Prozess möglich, bevor die GUI erstellt wird, was zu Problemen führen kann. In anderen Fällen sind die Erstellung von Orchestrierungs -GUI -Erstellungen normalerweise die letzten im anfänglichen Thread, also verwenden beide Invokelater oder Invokeandwait.
Warum erstellt der anfängliche Thread die GUI nicht direkt? Denn fast alle Code, die zum Erstellen und Interagieren mit Schwungkomponenten verwendet werden, müssen im Ereignis -Versand -Thread ausgeführt werden. Diese Einschränkung wird unten erörtert.
2. Ereignisverteilungs -Thread
Der Verarbeitungscode des Swing -Ereignisses wird in einem speziellen Thread ausgeführt, der als Event -Versand -Thread bezeichnet wird. Der größte Teil des Code, der die Swing -Methode aufruft, wird in diesem Thread ausgeführt. Dies ist notwendig, da die meisten Swing-Objekte "nicht thread sicher" sind.
Sie können sich die Ausführung von Code als eine Reihe von kurzen Aufgaben im Event -Versand -Thread vorstellen. Die meisten Aufgaben werden durch Ereignisbearbeitungsmethoden wie ActionListener.Actionperformed aufgerufen. Die verbleibenden Aufgaben werden vom Programmcode unter Verwendung von Invokelater oder Invokeandwait orchestriert. Aufgaben im Event -Versand -Thread müssen schnell ausgeführt werden können. Andernfalls werden unverarbeitete Ereignisse zurückgelegt und die Benutzeroberfläche wird "reaktionsschnell".
Wenn Sie feststellen müssen, ob Ihr Code im Event -Versand -Thread ausgeführt wird, rufen Sie javax.swing.swingutilities.isventDispatchThread an.
3. Arbeiterfäden und Swingworker
Wenn ein Swing -Programm eine lange Aufgabe ausführen muss, wird es normalerweise mit einem Worker -Thread erledigt. Jede Aufgabe wird in einem Worker -Thread ausgeführt, der eine Instanz der Klasse von Javax.swing.swingworker ist. Die Swingworker -Klasse ist eine abstrakte Klasse; Sie müssen seine Unterklasse definieren, um ein Swingworker -Objekt zu erstellen. Normalerweise verwenden Sie dazu anonyme innere Klassen.
Swingworker bietet einige Kommunikations- und Kontrollfunktionen:
(1) Die Unterklasse des Swingworker kann eine Methode definieren. Wenn die Hintergrundaufgabe abgeschlossen ist, wird sie automatisch vom Event -Versand -Thread aufgerufen.
(2) Swingworker Class implementiert java.util.concurrent.future. Diese Schnittstelle ermöglicht Hintergrundaufgaben, einen Rückgabewert für andere Threads bereitzustellen. Die Methoden in dieser Schnittstelle liefern auch die Funktionen, die das Rückgängigmachen von Hintergrundaufgaben ermöglichen und feststellen, ob die Hintergrundaufgaben erledigt oder widerrufen wurden.
(3) Hintergrundaufgaben können Zwischenergebnisse liefern, indem Sie Swingworker anrufen.
(4) Hintergrundaufgaben können Bindungseigenschaften definieren. Durch Änderungen des Bindungsattributs werden Ereignisse ausgelöst, und der Ereignis -Versand -Thread ruft den Ereignishandler auf, um diese ausgelösten Ereignisse zu verarbeiten.
4. Einfache Backend -Aufgaben
Hier ist ein Beispiel: Diese Aufgabe ist sehr einfach, ist aber eine potenziell zeitaufwändige Aufgabe. Tumbeil Applet importiert eine Reihe von Bilddateien. Wenn diese Bilddateien über den ersten Thread importiert werden, wird eine Verzögerung vorhanden, bevor die GUI angezeigt wird. Wenn diese Bilddateien im Ereignis -Versand -Thread importiert werden, kann die GUI vorübergehend nicht antworten.
Um diese Probleme zu lösen, erstellt und führt die Tumbsitem -Klasse eine Instanz der Stringworker -Klasse bei, wenn sie initialisiert wird. Die Doinbackground -Methode dieses Objekts wird in einem Worker -Thread ausgeführt, das Bild in ein ImageIcon -Array importiert und gibt einen Verweis darauf zurück. Anschließend wird die gemessene Methode im Ereignis -Versand -Thread ausgeführt, die zurückgegebene Referenz abrufen und in die IMGS der Mitgliedsvariable der Applet -Klasse einfügen. Wenn Sie dies tun, kann die TumbSeitem -Klasse sofort eine GUI erstellen, ohne dass der Import -Import abgeschlossen ist.
Der folgende Beispielcode definiert und implementiert ein Swingworker -Objekt.
Swingworker Worker = neuer Swingworker <imageicon [], void> () {@Override public imageicon [] doInbackground () {Final imageicon [] innerimgs = new ImageCon [nimgs]; für (int i = 0; i <nimgs; i ++) {innerimgs [i] = loadImage (i+1); } return Innerimgs; } @Override public void Done () {// Entfernen Sie das Lakladen der Bilder. animator.removeall (); Loopslot = -1; try {imgs = get (); } catch (interruptedException ignorore) {} catch (java.util.concurrent.executionException e) {String warum = null; Throwable cause = e.getCause (); if (cause! = null) {warum = cause.getMessage (); } else {warum = e.getMessage (); } System.err.println ("Fehlerabzündungsdatei:" + warum); }}}}; Alle von Swingworker geerbten Unterklassen müssen den Doinbackground implementieren. Die Implementierung der gemessenen Methode ist optional.
Beachten Sie, dass Swingworker eine Paradigmenklasse mit zwei Parametern ist. Der erste Typ -Parameter gibt den Rückgabetyp des Doinbackgrounds an. Es handelt sich auch um eine Art von GET -Methode, die von anderen Threads aufgerufen werden kann, um den Rückgabewert von Doinbackground zu erhalten. Der Parameter des zweiten Typs gibt den Typ des Zwischenergebnisses an. In diesem Beispiel wird das Zwischenergebnis nicht zurückgegeben, daher ist es auf void eingestellt.
Mit der GET -Methode können Sie im Ereignis -Versand -Thread den Hinweis auf das Objekt -IMGs (im Worker -Thread erstellt) verwenden. Dies ermöglicht die Freigabe von Objekten zwischen Threads.
Es gibt tatsächlich zwei Möglichkeiten, das Objekt von der Doinbackground -Klasse zurückzugeben.
(1) Es gibt keine Parameter, um Swingworker.get aufzurufen. Wenn die Hintergrundaufgabe nicht abgeschlossen ist, blockiert die GET -Methode, bis sie abgeschlossen ist.
(2) Rufen Sie Swingworker an. Wenn die Hintergrundaufgabe nicht abgeschlossen ist, blockieren Sie, bis sie abgeschlossen ist - es sei denn, die Zeitüberschreitung läuft ab. In diesem Fall werfen Sie einen java.util.concurrent.timeoutexception aus.
5. Aufgaben mit mittleren Ergebnissen
Es ist nützlich, dass eine berufstätige Backend -Aufgabe Intermediate -Ergebnisse liefert. Backend -Aufgaben können den Swingworker anrufen. Diese Methode akzeptiert viele Parameter. Jeder Parameter muss einer sein, der durch den zweiten Typ -Parameter des Swingworker angegeben wird.
Der Swingworker.process kann überschrieben werden, um die von der Veröffentlichungsmethode bereitgestellten Ergebnisse zu speichern. Diese Methode wird vom Ereignis -Versand -Thread aufgerufen. Das Ergebnis der Veröffentlichungsmethode wird normalerweise nach einer Prozessmethode erfasst.
Schauen wir uns die Beispiele von filpper.java an. Dieses Programm generiert eine Reihe von zufälligen booleschen Tests Java.util.Random durch eine Hintergrundaufgabe. Es ist wie ein Münzwurfsexperiment. Um ihre Ergebnisse zu melden, verwendet die Hintergrundaufgabe ein Objektflippair.
private statische Klasse Flippair {Private Final Long Heads, Total; Flippair (lange Köpfe, lange Gesamtzahl) {this.heads = köpfe; this.total = total; }} Köpfe repräsentieren das Ergebnis von True; Die Gesamtsumme repräsentiert die Gesamtzahl der Würfe.
Das Hintergrundprogramm ist eine Instanz von Filptask:
Private Class Fliptask erweitert Swingworker <void, flippair> {
Da die Aufgabe kein Endergebnis zurückgibt, müssen Sie nicht angeben, welcher Parameter des ersten Typs ist, verwenden Sie void. Nach jedem "Werfen" werden die Aufgabe veröffentlicht:
@OverrideProtected void doInbackground () {Long Heads = 0; langes Gesamtbetrag = 0; Random random = new random (); while (! iscancelled ()) {Total ++; if (random.Nextboolean ()) {Heads ++; } veröffentlichen (New Flippair (Leiter, insgesamt)); } return null;} Da die Veröffentlichung häufig aufgerufen wird, werden viele Flippair -Werte gesammelt, bevor die Prozessmethode vom Ereignis -Versand -Thread aufgerufen wird. Der Prozess konzentriert sich nur auf den letzten Satz von Werten, die jedes Mal zurückgegeben wurden, und verwendet ihn, um die GUI zu aktualisieren:
geschützter void -Prozess (Listenpaare) {flippair pair = pairs.get (pairs.size () - 1); Kopfstext.SetText (string.format ("%d", pair.heads)); TotalText.setText (string.format ("%d", pair.total)); devText.setText (string.format ("%. 10g", (doppelt) pair.heads)/((doppelt) pair.total) - 0,5));} 6. Die Hintergrundaufgabe abbrechen
Rufen Sie Swingworker.cancel an, um eine ausführende Hintergrundaufgabe zu stornieren. Die Aufgabe muss mit ihrem eigenen Rückgängigmechanismus übereinstimmen. Es gibt zwei Möglichkeiten, dies zu tun:
(1) Wenn ein Interrupt empfangen wird, wird er gekündigt.
(2) Rufen Sie Swingworker.iscanceled an. Wenn Swingworker -Anrufe abbrechen, wird die Methode true zurückgegeben.
7. Binden Sie Attribute und Zustandsmethoden
Swingworker unterstützt gebundene Eigenschaften, was bei der Kommunikation mit anderen Threads sehr nützlich ist. Bietet zwei Bindungseigenschaften: Fortschritt und Zustand. Fortschritt und Zustand können verwendet werden, um Ereignisverarbeitungsaufgaben in Ereignisversandsthreads auszulösen.
Durch die Implementierung eines Immobilienwechsel -Hörers kann das Programm Änderungen, staatliche oder andere Bindungseigenschaften aufnehmen.
7.1Die Fortschrittsvariable
Die Fortschrittsbindungsvariable ist eine Ganzzahlvariable mit einem Bereich von 0 bis 100. Sie definierte den Setter (der geschützte Swingworker.SetProgress) und den Getter (der öffentliche Swingworker.GetProgress).
7.2 Die staatlich gebundene Variable
Änderungen der staatlichen Bindungsvariablen spiegeln den Prozess der Änderung des Swingworker -Objekts während seines Lebenszyklus wider. Diese Variable enthält eine Aufzählungstyp von Swingworker.StateValue. Mögliche Werte sind:
(1) ausstehend
Dieser Zustand dauert eine Zeit, um aus der Erstellung des Objekts zu wissen, dass die Doinbackground -Methode aufgerufen wird.
(2) begann
Dieser Zustand dauert einen Moment, bevor die Doinbackground -Methode aufgerufen wird, bis die gemessene Methode aufgerufen wird.
(3) fertig
Die verbleibende Zeit, die das Objekt existiert, bleibt in diesem Zustand.
Wenn Sie den Wert des aktuellen Zustands zurücksenden müssen, können Sie Swingworker.getState anrufen.
7.3Status -Methoden
Zwei von der zukünftige Schnittstelle bereitgestellte Methoden können auch den Status von Hintergrundaufgaben melden. Wenn die Aufgabe abgesagt wird, gibt iscancelled true zurück. Wenn die Aufgabe erledigt ist, d. H. Sie entweder normal abgeschlossen oder abgebrochen wird, gibt Isdone True zurück.
Verwenden von Behälter auf oberster Ebene
Swing bietet 3 oberste Containerklassen: JFrame, Jdialog, Japplet. Wenn Sie diese drei Klassen verwenden, müssen Sie auf die folgenden Punkte achten:
(1). Um auf dem Bildschirm angezeigt zu werden, muss jede GUI -Komponente Teil der enthaltenen Hierarchie sein. Die Einschlusshierarchie ist eine Baumstruktur der Komponente, und der Container der obersten Ebene ist seine Wurzel.
(2). Jede GUI -Komponente kann nur einmal eingeschlossen werden. Wenn sich eine Komponente bereits in einem Container befindet und dann versucht, sie einem neuen Container hinzuzufügen, wird die Komponente aus dem ersten Container entfernt und dem zweiten Container hinzugefügt.
(3). Jeder Container auf oberster Ebene verfügt über einen Inhaltsbereich. Im Allgemeinen enthält dieses Inhaltsfeld (direkt oder indirekt) alle visuellen Komponenten der Container-GUI auf der obersten Ebene.
(4). Sie können den oberen Container eine Menüleiste hinzufügen. Normalerweise befindet sich diese Menüleiste im oberen Container, aber außerhalb des Inhaltsfeldes.
1. Container und Einschlusshierarchien auf höchstem Niveau
Jedes Programm, das die Swing-Komponente verwendet, verfügt über mindestens einen Container der obersten Ebene. Dieser Container der obersten Ebene ist der Root-Knoten, der die Hierarchie enthält. Diese Hierarchie enthält alle Schwungkomponenten, die in diesem Container der obersten Ebene angezeigt werden.
In der Regel verfügt eine separate Swing-GUI-basierte Anwendung über mindestens eine Einschlusshierarchie und ihr Wurzelknoten ist ein JFrame. Wenn beispielsweise eine Anwendung über ein Fenster und zwei Dialogfelder verfügt, verfügt die Anwendung über drei Einschlussniveaus, dh drei Container der obersten Ebene. Eine Containment -Hierarchie nimmt JFrame als Wurzelknoten, und zwei andere Containmenthierarchys haben jeweils einen Jdialog als Wurzelknoten.
Ein Applet, das auf Schwungkomponenten basiert, enthält mindestens eine Einschlusshierarchie, und es kann festgestellt werden, dass einer von ihnen mit einem Japplet als Wurzelknoten hergestellt werden muss. Zum Beispiel wird ein Applet mit einem Dialogfeld zwei Einschlussniveaus haben. Die Komponente im Browserfenster wird in eine Containment -Hierarchie platziert und sein Wurzelknoten ist ein Japplet -Objekt. Das Dialogfeld verfügt über eine Hierarchie, und sein Wurzelknoten ist ein JDIALOG -Objekt.
2. Fügen Sie dem Inhaltsfeld Komponenten hinzu
Der folgende Codevorgang besteht darin, das Inhaltspanel des Frame im obigen Beispiel zu erhalten und ein gelbes Etikett hinzuzufügen:
Frame.GetContentPane (). Add (Yellowlabel, BorderLayout.Center);
Wie im Code gezeigt, müssen Sie zunächst das Inhaltsfeld des obersten Containers finden und über die Methode GetContentPane implementieren. Das Standardinhaltspanel ist ein einfacher Zwischencontainer, der von JComponent unter Verwendung eines BorderLayout als Panel -Manager erbt.
Das Anpassen eines Inhaltsbereichs ist einfach - Einrichten Sie einen Panel -Manager oder fügen Sie Grenzen hinzu. Es muss hier angemerkt werden, dass die GetContentPane -Methode ein Containerobjekt zurückgibt, nicht ein JComponent -Objekt. Dies bedeutet, dass Sie, wenn Sie einige der Funktionen des JComponent nutzen müssen, auch den Rückgabewert konvertieren oder Ihre eigene Komponente als Inhaltsbereich erstellen müssen. Unser Beispiel verwendet normalerweise die zweite Methode. Weil die zweite Methode klarer und klarer ist. Eine andere Möglichkeit, die wir manchmal verwenden, besteht darin, dem Inhaltspanel einfach eine selbstdefinierte Komponente hinzuzufügen, um das Inhaltspanel vollständig abzudecken.
Wenn Sie Ihr eigenes Inhaltsbereich erstellen, achten Sie darauf, dass es undurchsichtig ist. Ein undurchsichtiger JPanel wird eine gute Wahl sein. Beachten Sie, dass das Layout von JPanel standardmäßig als FlowLayout verwaltet wird und Sie es möglicherweise durch einen anderen Layout -Manager ersetzen möchten.
Damit eine Komponente ein Inhaltspanel ist, müssen Sie beispielsweise die Methode für die SetContentPane des obersten Containers verwenden:
// Erstellen Sie ein Panel und fügen Sie Komponenten hinzu. Pane.//contentpane.setopaque(True);ToplevelContainer.SetContentPane(ContentPane);
HINWEIS: Verwenden Sie keine transparenten Container als Inhaltskollektoren wie JScrollPane, JSplitPane und JTabbedPane. Durch ein transparentes Inhaltsfeld werden Komponenten verwechselt. Obwohl Sie durch die Methode Setopaque (True) eine transparente Schwungkomponente undurchsichtig machen können, sieht sie nicht richtig aus, wenn einige Komponenten so eingestellt sind, dass sie vollständig undurchsichtig sind. Zum Beispiel ein Tag -Panel.
3. Hinzufügen einer Menüleiste
Theoretisch kann jeder Container auf oberster Ebene eine Menüleiste haben. Die Fakten zeigen jedoch, dass die Menüleiste nur in Frame oder Applet angezeigt wird. Um dem obersten Container eine Menüleiste hinzuzufügen, müssen Sie ein JMenuuBar-Objekt erstellen, einige Menüs zusammenstellen und dann die SetJMenuuBar-Methode aufrufen. Die ToplevelDemo -Instanz fügt über den folgenden Code eine Menüleiste zu seinem Frame hinzu.
Frame.SetJMenubar (Cyanmenubar);
4. Die Wurzelscheibe
Jeder Behälter auf oberster Ebene basiert auf einem impliziten Zwischenbehälter, der als Wurzelbehälter bezeichnet wird. Dieser Root Container verwaltet die Inhaltspanel und die Menüleiste zusammen mit zwei oder mehr anderen Containern (siehe Schichtbereich usw.). Normalerweise müssen Sie den Wurzelbehälter der Schwungkomponenten nicht wissen. Wenn Sie jedoch einen Mausklick abfangen oder auf mehrere Komponenten zeichnen möchten, müssen Sie den Stammbehälter kennen.
Das obige wurde über das Inhaltsfeld und die optionale Menüleiste beschrieben und wird hier nicht wiederholt. Die beiden anderen im Wurzelbehälter enthaltenen Komponenten sind die Layout -Panel und die Glasplatte. Das Layout -Bereich enthält direkt das Menüfeld und das Inhaltsfeld und ermöglicht es Ihnen, die anderen von Z -Koordinaten hinzugefügten Komponenten zu sortieren. Glasschirme werden normalerweise verwendet, um Eingangsaktionen in der oberen Schicht abzufangen, und können auch zum Zeichnen mehrerer Komponenten verwendet werden.