Freigeben über


Zusammenfassung von Kapitel 20: Asynchrone Verarbeitung und Datei-E/A

Hinweis

Dieses Buch wurde im Frühjahr 2016 veröffentlicht und seitdem nicht aktualisiert. Wenngleich ein großer Teil des Buchs weiterhin relevante Informationen liefert, sind einige Abschnitte veraltet, und einige Themen sind nicht mehr korrekt oder vollständig.

Eine grafische Benutzeroberfläche muss sequenziell auf Benutzereingabeereignisse reagieren. Dies impliziert, dass die gesamte Verarbeitung von Benutzereingabeereignissen in einem einzigen Thread erfolgen muss, der häufig als Hauptthread oder UI-Thread bezeichnet wird.

Benutzer erwarten von grafischen Benutzeroberflächen eine kurze Reaktionszeit. Dies bedeutet, dass ein Programm Benutzereingabeereignisse schnell verarbeiten muss. Wenn dies nicht möglich ist, muss die Verarbeitung auf sekundäre Verarbeitungsthreads verlagert werden.

In verschiedenen Beispielprogrammen in diesem Buch wurde die WebRequest-Klasse verwendet. In dieser Klasse startet die BeginGetResponse-Methode einen Arbeitsthread, der nach seinem Abschluss eine Rückruffunktion aufruft. Diese Rückruffunktion wird jedoch im Arbeitsthread ausgeführt, sodass das Programm die Device.BeginInvokeOnMainThread-Methode aufrufen muss, um auf die Benutzerschnittstelle zuzugreifen.

Hinweis

Für Xamarin.Forms-Programme sollte HttpClient anstelle von WebRequest für den Zugriff auf Dateien über das Internet verwendet werden. HttpClient unterstützt asynchrone Vorgänge.

Ein moderner Ansatz für die asynchrone Verarbeitung steht in .NET und C# zur Verfügung. Dies schließt die Klassen Task und Task<TResult>, weitere Typen in den Namespaces System.Threading und System.Threading.Tasks sowie die C# 5.0-Schlüsselwörter async und await ein. Darauf konzentriert sich dieses Kapitel.

Von Rückrufen zu „await“

Die Page-Klasse selbst enthält drei asynchrone Methoden zur Anzeige von Warnfeldern:

Die Task-Objekte weisen darauf hin, dass diese Methoden das aufgabenbasierte asynchrone Muster (Task-based Asynchronous Pattern, TAP) verwenden. Diese Task-Objekte werden von der Methode schnell zurückgegeben. Die Task<T>-Rückgabewerte stellen eine „Zusage“ (Promise) dar, dass ein Wert vom Typ TResult zur Verfügung steht, wenn die Aufgabe abgeschlossen wurde. Der Task-Rückgabewert weist auf eine asynchrone Aktion hin, die ausgeführt wird, aber keinen Wert zurückgibt.

In all diesen Fällen ist Task abgeschlossen, wenn der Benutzer das Warnfeld schließt.

Eine Warnung mit Rückrufen

Das Beispiel AlertCallbacks veranschaulicht, wie Task<bool>-Rückgabeobjekte und Device.BeginInvokeOnMainThread-Aufrufe mithilfe von Rückrufmethoden verarbeitet werden.

Warnung mit Lambda-Ausdrücken

Das Beispiel AlertLambdas veranschaulicht, wie anonyme Lambda-Funktionen zum Verarbeiten von Task- und Device.BeginInvokeOnMainThread-Aufrufen verwendet werden.

Warnung mit „await“

Ein geradlinigerer Ansatz umfasst die in C# 5 eingeführten Schlüsselwörter async und await. Ihre Verwendung wird im Beispiel AlertAwait veranschaulicht.

Warnung mit „nothing“

Wenn die asynchrone Methode Task anstelle von Task<TResult> zurückgibt, muss das Programm keine dieser Techniken verwenden, wenn es nicht wissen muss, wann die asynchrone Aufgabe abgeschlossen ist. Dies wird im Beispiel NothingAlert gezeigt.

Asynchrones Speichern der Programmeinstellungen

Das Beispiel SaveProgramChanges veranschaulicht die Verwendung der SavePropertiesAsync-Methode von Application zum Speichern von Programmeinstellungen, wenn sich diese ohne Überschreiben der OnSleep-Methode ändern.

Plattformunabhängiger Zeitgeber

Es ist möglich, mit Task.Delay einen plattformunabhängigen Zeitgeber zu erstellen. Dies wird im Beispiel TaskDelayClock gezeigt.

Dateieingabe/-ausgabe

Traditionell war der .NET-Namespace System.IO die Quelle zur Unterstützung der Datei-E/A. Einige Methoden in diesem Namensraum unterstützen asynchrone Vorgänge, die meisten anderen jedoch nicht. Der Namensraum unterstützt außerdem mehrere einfache Methodenaufrufe, die anspruchsvolle Datei-E/A-Funktionen ausführen.

Gute und schlechte Nachrichten

Alle Plattformen, die von lokalem Xamarin.Forms Anwendungsspeicher unterstützt werden – Speicher, der privat für die Anwendung ist.

Die Xamarin.iOS- und Xamarin.Android-Bibliotheken umfassen eine Version von .NET, die Xamarin explizit auf diese zwei Plattformen zugeschnitten hat. Sie enthalten Klassen von System.IO, die Sie zum Ausführen von Datei-E/A-Vorgängen mit dem lokalen Anwendungsspeicher in diesen zwei Plattformen nutzen können.

Wenn Sie aber in einer Xamarin.Forms-PCL nach diesen System.IO-Klassen suchen, werden Sie sie nicht finden. Das Problem ist, dass Microsoft die Datei-E/A für die Windows-Runtime-API komplett überarbeitet hat. Programme, die auf Windows 8.1, Windows Phone 8.1 und die universelle Windows-Plattform abzielen, verwenden System.IO nicht für Datei-E/A.

Dies bedeutet, dass Sie die DependencyService (zuerst in Kapitel 9 erläutert) verwenden müssen. Plattformspezifische API-Aufrufe zum Implementieren von Datei-E/A.

Hinweis

Portable Klassenbibliotheken wurden durch .NET Standard 2.0-Bibliotheken ersetzt, und .NET Standard 2.0 unterstützt System.IO-Typen für alle Xamarin.Forms-Plattformen. Für die meisten Datei-E/A-Aufgaben ist es nicht länger nötig, einen DependencyService zu verwenden. Informationen zu einem moderneren Ansatz für die Datei-E/A finden Sie unter Dateiverarbeitung in Xamarin.Forms.

Ein erster Versuch einer plattformübergreifenden Datei-E/A

Das Beispiel TextFileTryout definiert eine IFileHelper-Schnittstelle für Datei-E/A und Implementierungen dieser Schnittstelle auf allen Plattformen. Die Windows-Runtime-Implementierungen funktionieren jedoch nicht mit den Methoden in dieser Schnittstelle, weil die Datei-E/A-Methoden der Windows-Runtime asynchron sind.

Verarbeiten von Datei-E/A-Vorgängen in der Windows-Runtime

Programme, die unter der Windows-Runtime ausgeführt werden, verwenden Klassen in den Namespaces Windows.Storage und Windows.Storage.Streams für Datei-E/A-Vorgänge, einschließlich des lokalen Speichers der Anwendung. Da Microsoft festgelegt hat, dass jeder Vorgang mit einer Dauer von mehr als 50 Millisekunden asynchron sein sollte, um den UI-Thread nicht zu blockieren, sind diese Datei-E/A-Methoden meist asynchron.

Der Code zur Veranschaulichung dieses neuen Ansatzes ist in einer Bibliothek enthalten, damit er von anderen Anwendungen verwendet werden kann.

Plattformspezifische Bibliotheken

Es ist vorteilhaft, wiederverwendbaren Code in Bibliotheken zu speichern. Dies ist natürlich schwieriger, wenn verschiedene Teile des wiederverwendbaren Codes für völlig unterschiedliche Betriebssysteme bestimmt sind.

Die Projektmappe Xamarin.FormsBook.Platform zeigt einen Ansatz. Diese Projektmappe enthält verschiedene Projekte:

Alle Plattformprojekte (mit Ausnahme von Xamarin.FormsBook.Platform.WinRT) enthalten Verweise auf Xamarin.FormsBook.Platform. Die drei Windows-Projekte enthalten einen Verweis auf Xamarin.FormsBook.Platform.WinRT.

Alle Projekte enthalten eine statische Toolkit.Init-Methode, um sicherzustellen, dass die Bibliothek geladen wird, wenn von einem Projekt in einer Xamarin.Forms-Anwendungsprojektmappe nicht direkt darauf verwiesen wird.

Das Projekt Xamarin.FormsBook.Platform enthält die neue IFileHelper-Schnittstelle. Alle Methoden weisen jetzt Namen mit Async-Suffixen auf und geben Task-Objekte zurück.

Das Projekt Xamarin.FormsBook.Platform.WinRT enthält die FileHelper-Klasse für die Windows-Runtime.

Das Projekt Xamarin.FormsBook.Platform.iOS enthält die FileHelper-Klasse für iOS. Diese Methoden müssen ab sofort asynchron sein. Einige der Methoden verwenden die asynchronen Versionen der in StreamWriter und StreamReader definierten Methoden: WriteAsync und ReadToEndAsync. Andere konvertieren ein Ergebnis mithilfe der FromResult-Methode in ein Task-Objekt.

Das Projekt Xamarin.FormsBook.Platform.Android enthält eine ähnliche FileHelper-Klasse für Android.

Das Projekt Xamarin.FormsBook.Platform enthält ebenfalls eine FileHelper-Klasse, die die Verwendung des DependencyService-Objekts vereinfacht.

Um diese Bibliotheken zu verwenden, muss eine Anwendungsprojektmappe alle Projekte in der Projektmappe Xamarin.FormsBook.Platform enthalten, und jedes der Anwendungsprojekte muss einen Verweis auf die entsprechende Bibliothek in Xamarin.FormsBook.Platform enthalten.

Die Projektmappe TextFileAsync veranschaulicht die Verwendung der Xamarin.FormsBook.Platform-Bibliotheken. Jedes der Projekte enthält einen Aufruf an Toolkit.Init. Die Anwendung nutzt asynchrone Datei-E/A-Funktionen.

Ausführung im Hintergrund

Methoden in Bibliotheken, die Aufrufe mehrerer asynchroner Methoden ( z. B. der WriteFileAsync Methoden ReadFileASync in der Windows-Runtime-Klasse FileHelper ) ausführen, können mithilfe der ConfigureAwait Methode etwas effizienter ausgeführt werden, um den Wechsel zum Benutzeroberflächenthread zu vermeiden.

Keine Blockierung des UI-Treads!

Gelegentlich ist es verlockend, die Verwendung von ContinueWith oder await zu vermeiden, indem man die Eigenschaft Result auf die Methoden anwendet. Dies sollte vermieden werden, da es den UI-Thread blockieren oder sogar dazu führen kann, dass die Anwendung nicht mehr reagiert.

Eigene await-fähige Methoden

Sie können einigen Code asynchron ausführen, indem Sie ihn an eine der Task.Run-Methoden übergeben. Sie können Task.Run innerhalb einer asynchronen Methode aufrufen, die einen Teil der Arbeit übernimmt.

Die verschiedenen Task.Run-Muster werden nachfolgend diskutiert.

Die grundlegende Mandelbrot-Menge

Um die Mandelbrot-Menge in Echtzeit zu zeichnen, umfasst die Xamarin.Forms.Toolkit-Bibliothek eine Complex-Struktur, die der im Namespace System.Numerics ähnelt.

Das MandelbrotSet-Beispiel enthält eine CalculateMandeblotAsync-Methode in der zugehörigen CodeBehind-Datei, die die grundlegende Schwarz-Weiß-Mandelbrot-Menge berechnet und BmpMaker zu deren Platzierung auf einer Bitmap verwendet.

Melden des Fortschritts

Um den Fortschritt einer asynchronen Methode zu melden, können Sie eine Progress<T>-Klasse instanziieren und Ihre asynchrone Methode so definieren, dass sie ein Argument vom Typ IProgress<T> aufweist. Dies wird im Beispiel MandelbrotProgress gezeigt.

Abbrechen des Auftrags

Sie können eine asynchrone Methode auch so schreiben, dass sie abgebrochen werden kann. Sie beginnen mit einer Klasse namens CancellationTokenSource. Die Token-Eigenschaft ist ein Wert vom Typ CancellationToken. Dieser Wert wird an eine asynchrone Funktion übergeben. Ein Programm ruft die Cancel-Methode von CancellationTokenSource auf (im Allgemeinen als Reaktion auf eine Benutzeraktion), um die asynchrone Funktion abzubrechen.

Die asynchrone Methode kann in regelmäßigen Abständen die Eigenschaft IsCancellationRequested von CancellationToken überprüfen und beendet werden, wenn die Eigenschaft true lautet, oder einfach die Methode ThrowIfCancellationRequested aufrufen. In letzterem Fall wird die Methode mit einer OperationCancelledException beendet.

Das MandelbrotCancellation-Beispiel veranschaulicht die Verwendung einer Funktion, die abgebrochen werden kann.

Mandelbrot (MVVM)

Das MandelbrotXF-Beispiel umfasst eine umfangreichere Benutzerschnittstelle und basiert größtenteils auf den Klassen MandelbrotModel und MandelbrotViewModel:

Drei Screenshots von MandelbrotXF

Zurück zum Web

Die in einigen Beispielen verwendete WebRequest-Klasse nutzt ein altmodisches asynchrones Protokoll: APM (Asynchronous Programming Model). Sie können eine solche Klasse in das moderne TAP-Protokoll konvertieren, indem Sie eine der FromAsync-Methoden in der TaskFactory-Klasse verwenden. Dies wird im Beispiel ApmToTap veranschaulicht.